| # |
| # linux_logo in x86_64 assembly language |
| # based on the code from ll_asm-0.36 |
| # |
| # By Vince Weaver <vince _at_ deater.net> |
| # |
| # Modified to remove non-deterministic system calls |
| # And to avoid reading from /proc |
| # |
| |
| |
| .include "logo.include" |
| |
| # offsets into the results returned by the uname syscall |
| .equ U_SYSNAME,0 |
| .equ U_NODENAME,65 |
| .equ U_RELEASE,65*2 |
| .equ U_VERSION,(65*3) |
| .equ U_MACHINE,(65*4) |
| .equ U_DOMAINNAME,65*5 |
| |
| # offset into the results returned by the sysinfo syscall |
| .equ S_TOTALRAM,32 |
| |
| # Sycscalls |
| .equ SYSCALL_EXIT, 60 |
| .equ SYSCALL_READ, 0 |
| .equ SYSCALL_WRITE, 1 |
| .equ SYSCALL_OPEN, 2 |
| .equ SYSCALL_CLOSE, 3 |
| .equ SYSCALL_SYSINFO, 99 |
| .equ SYSCALL_UNAME, 63 |
| |
| # |
| .equ STDIN,0 |
| .equ STDOUT,1 |
| .equ STDERR,2 |
| |
| .globl _start |
| _start: |
| #========================= |
| # PRINT LOGO |
| #========================= |
| |
| # LZSS decompression algorithm implementation |
| # by Stephan Walter 2002, based on LZSS.C by Haruhiko Okumura 1989 |
| # optimized some more by Vince Weaver |
| |
| # we used to fill the buffer with FREQUENT_CHAR |
| # but, that only gains us one byte of space in the lzss image. |
| # the lzss algorithm does automatic RLE... pretty clever |
| # so we compress with NUL as FREQUENT_CHAR and it is pre-done for us |
| |
| mov $(N-F), %ebp # R |
| |
| mov $logo, %esi # %esi points to logo (for lodsb) |
| |
| mov $out_buffer, %edi # point to out_buffer |
| push %rdi # save this value for later |
| |
| xor %ecx, %ecx |
| |
| decompression_loop: |
| lodsb # load in a byte |
| |
| mov $0xff, %bh # re-load top as a hackish 8-bit counter |
| mov %al, %bl # move in the flags |
| |
| test_flags: |
| cmp $logo_end, %esi # have we reached the end? |
| je done_logo # ! if so, exit |
| |
| shr $1, %ebx # shift bottom bit into carry flag |
| jc discrete_char # ! if set, we jump to discrete char |
| |
| offset_length: |
| lodsw # get match_length and match_position |
| mov %eax,%edx # copy to edx |
| # no need to mask dx, as we do it |
| # by default in output_loop |
| |
| shr $(P_BITS),%eax |
| add $(THRESHOLD+1),%al |
| mov %al,%cl # cl = (ax >> P_BITS) + THRESHOLD + 1 |
| # (=match_length) |
| |
| output_loop: |
| and $POSITION_MASK,%dh # mask it |
| mov text_buf(%rdx), %al # load byte from text_buf[] |
| inc %edx # advance pointer in text_buf |
| store_byte: |
| stosb # store it |
| |
| mov %al, text_buf(%rbp) # store also to text_buf[r] |
| inc %ebp # r++ |
| and $(N-1), %bp # mask r |
| |
| loop output_loop # repeat until k>j |
| |
| or %bh,%bh # ! if 0 we shifted through 8 and must |
| jnz test_flags # re-load flags |
| |
| jmp decompression_loop |
| |
| discrete_char: |
| lodsb # load a byte |
| inc %ecx # we set ecx to one so byte |
| # will be output once |
| # (how do we know ecx is zero?) |
| |
| jmp store_byte # and cleverly store it |
| |
| |
| # end of LZSS code |
| |
| done_logo: |
| |
| pop %rbp # get out_buffer and keep in bp |
| mov %ebp,%ecx # move out_buffer to ecx |
| |
| call write_stdout # print the logo |
| |
| # |
| # Setup |
| # |
| setup: |
| mov $strcat,%edx # use rdx as call pointer (smaller op) |
| |
| |
| #========================== |
| # PRINT VERSION |
| #========================== |
| |
| # push $SYSCALL_UNAME # uname syscall |
| # pop %rax # in 3 bytes |
| mov $uname_info,%edi # uname struct (0 extend address) |
| # syscall # do syscall |
| |
| mov %ebp,%edi # point %edi to out_buffer |
| |
| mov $(uname_info+U_SYSNAME),%esi # os-name from uname "Linux" |
| call *%rdx # call strcat |
| |
| mov $ver_string,%esi # source is " Version " |
| call *%rdx # call strcat |
| push %rsi # save our .txt pointer |
| |
| mov $(uname_info+U_RELEASE),%esi # version from uname "2.4.1" |
| call *%rdx # call strcat |
| |
| pop %rsi # restore .txt pointer |
| # source is ", Compiled " |
| call *%rdx # call strcat |
| push %rsi # store for later |
| |
| mov $(uname_info+U_VERSION),%esi # compiled date |
| call *%rdx # call strcat |
| |
| mov %ebp,%ecx # move out_buffer to ecx |
| |
| mov $0xa,%ax # store linefeed on end |
| stosw # and zero |
| |
| call *%rdx # call strcat |
| |
| call center_and_print # center and print |
| |
| #=============================== |
| # Middle-Line |
| #=============================== |
| middle_line: |
| #========= |
| # Load /proc/cpuinfo into buffer |
| #========= |
| |
| push %rdx # save call pointer |
| |
| # push $SYSCALL_OPEN # load 5 [ open() ] |
| # pop %rax # in 3 bytes |
| |
| # mov $cpuinfo,%edi # '/proc/cpuinfo' |
| # xor %esi,%esi # 0 = O_RDONLY <bits/fcntl.h> |
| # cdq # clear edx in clever way |
| # syscall # syscall. fd in eax. |
| # we should check that eax>=0 |
| |
| # mov %eax,%edi # save our fd |
| |
| # xor %eax,%eax # SYSCALL_READ make== 0 |
| |
| mov $disk_buffer,%esi |
| |
| # mov $16,%dh # 4096 is maximum size of proc file #) |
| # we load sneakily by knowing |
| # 16<<8 = 4096. be sure edx clear |
| |
| # syscall |
| |
| # push $SYSCALL_CLOSE # close (to be correct) |
| # pop %rax |
| # syscall |
| |
| #============= |
| # Number of CPUs |
| #============= |
| number_of_cpus: |
| |
| xor %ebx,%ebx # chip count |
| |
| # $disk_buffer still in %rsi |
| bogo_loop: |
| mov (%rsi), %eax # load 4 bytes into eax |
| inc %esi # increment pointer |
| |
| cmp $0,%al # check for end of file |
| je done_bogo |
| |
| # Grrr, due to a bug in binutils 2.18.50.0.9 |
| # (which unfortunately shipped with Fedora 10) |
| # http://sourceware.org/bugzilla/show_bug.cgi?id=6878 |
| # We can't use the apostrophe character |
| |
| # cmp $('o'<<24+'g'<<16+'o'<<8+'b'),%eax |
| cmp $(0x6f<<24+0x67<<16+0x6f<<8+0x62),%eax |
| # "bogo" in little-endian |
| |
| jne bogo_loop # ! if not equal, keep going |
| add $2,%ebx # otherwise, we have a bogo |
| # 2 times too for future magic |
| jmp bogo_loop |
| |
| done_bogo: |
| lea one-6(%rbx,%rbx,2), %esi |
| # Load into esi |
| # [one]+(num_cpus*6) |
| # |
| # the above multiplies by three |
| # esi = (ebx+(ebx*2)) |
| # and we double-incremented ebx |
| # earlier |
| |
| mov %ebp,%edi # move output buffer to edi |
| |
| pop %rdx # restore call pointer |
| call *%rdx # copy it (call strcat) |
| |
| # mov $' ',%al # print a space |
| mov $0x20,%al # print a space |
| |
| stosb |
| |
| push %rbx |
| push %rdx # store strcat pointer |
| |
| #========= |
| # MHz |
| #========= |
| print_mhz: |
| # mov $('z'<<24+'H'<<16+'M'<<8+' '),%ebx |
| mov $(0x7a<<24+0x48<<16+0x4d<<8+0x20),%ebx |
| # find ' MHz' and grab up to . |
| # we are little endian |
| # mov $'.',%ah |
| mov $0x2e,%ah |
| |
| # below is same as "sub $(strcat-find_string),%edx |
| # gas won't let us force the one-byte constant |
| .byte 0x83,0xEA,strcat-find_string |
| |
| call *%rdx # call find string |
| |
| mov %ebx,%eax # clever way to get MHz in, sadly |
| ror $8,%eax # not any smaller than a mov |
| stosl |
| |
| #========= |
| # Chip Name |
| #========= |
| chip_name: |
| # mov $('e'<<24+'m'<<16+'a'<<8+'n'),%ebx |
| mov $(0x65<<24+0x6d<<16+0x61<<8+0x6e),%ebx |
| # find 'name\t: ' and grab up to \n |
| # we are little endian |
| # mov $' ',%ah |
| mov $0x20,%ah |
| call *%rdx # call find_string |
| stosb |
| call skip_spaces |
| |
| pop %rdx |
| pop %rbx # restore chip count |
| pop %rsi |
| |
| call *%rdx # ' Processor' |
| cmpb $2,%bl |
| jne print_s |
| inc %rsi # ! if singular, skip the s |
| print_s: |
| call *%rdx # 's, ' |
| |
| push %rsi # restore the values |
| push %rdx |
| |
| #======== |
| # RAM |
| #======== |
| |
| # push %rdi |
| # push $SYSCALL_SYSINFO # sysinfo() syscall |
| # pop %rax |
| # mov $sysinfo_buff,%edi |
| # syscall |
| # pop %rdi |
| |
| # The following has to be a 64 bit load, to support |
| # Ram > 4GB |
| mov (sysinfo_buff+S_TOTALRAM),%rax # size in bytes of RAM |
| shr $20,%rax # divide by 1024*1024 to get M |
| adc $0, %eax # round |
| |
| call num_to_ascii |
| |
| pop %rdx # restore strcat pointer |
| |
| pop %rsi # print 'M RAM, ' |
| call *%rdx # call strcat |
| |
| push %rsi |
| |
| #======== |
| # Bogomips |
| #======== |
| |
| # mov $('s'<<24+'p'<<16+'i'<<8+'m'),%ebx |
| mov $(0x73<<24+0x70<<16+0x69<<8+0x6d),%ebx |
| # find 'mips\t: ' and grab up to \n |
| mov $0xa,%ah |
| call find_string |
| |
| pop %rsi # bogo total follows RAM |
| |
| call *%rdx # call strcat |
| |
| push %rsi |
| |
| mov %ebp,%ecx # point ecx to out_buffer |
| |
| push %rcx |
| call center_and_print # center and print |
| |
| #================================= |
| # Print Host Name |
| #================================= |
| last_line: |
| mov %ebp,%edi # point to output_buffer |
| |
| mov $(uname_info+U_NODENAME),%esi # host name from uname() |
| call *%rdx # call strcat |
| |
| pop %rcx # ecx is unchanged |
| call center_and_print # center and print |
| |
| pop %rcx # (.txt) pointer to default_colors |
| |
| call write_stdout |
| |
| #================================ |
| # Exit |
| #================================ |
| exit: |
| push $SYSCALL_EXIT # Put exit syscall in rax |
| pop %rax |
| |
| xor %edi,%edi # Make return value $0 |
| syscall |
| |
| |
| #================================= |
| # FIND_STRING |
| #================================= |
| # ah is char to end at |
| # ebx is 4-char ascii string to look for |
| # edi points at output buffer |
| |
| find_string: |
| |
| mov $disk_buffer-1,%esi # look in cpuinfo buffer |
| find_loop: |
| inc %esi |
| cmpb $0, (%rsi) # are we at EOF? |
| je done # ! if so, done |
| |
| cmp (%rsi), %ebx # do the strings match? |
| jne find_loop # ! if not, loop |
| |
| # ! if we get this far, we matched |
| |
| find_colon: |
| lodsb # repeat till we find colon |
| cmp $0,%al |
| je done |
| # cmp $':',%al |
| cmp $0x3a,%al |
| jne find_colon |
| |
| skip_spaces: |
| lodsb # skip spaces |
| cmp $0x20,%al # Loser new intel chips have lots?? |
| je skip_spaces |
| |
| store_loop: |
| cmp $0,%al |
| je done |
| cmp %ah,%al # is it end string? |
| je almost_done # ! if so, finish |
| # cmp $'\n',%al |
| cmp $0xa,%al |
| je almost_done |
| stosb # ! if not store and continue |
| lodsb |
| |
| jmp store_loop |
| |
| almost_done: |
| movb $0, (%rdi) # replace last value with NUL |
| done: |
| ret |
| |
| |
| #================================ |
| # strcat |
| #================================ |
| |
| strcat: |
| lodsb # load a byte from [ds:esi] |
| stosb # store a byte to [es:edi] |
| cmp $0,%al # is it zero? |
| jne strcat # ! if not loop |
| dec %edi # point to one less than null |
| ret # return |
| |
| #============================== |
| # center_and_print |
| #============================== |
| # string to center in ecx |
| |
| center_and_print: |
| push %rdx # save strcat pointer |
| push %rcx # save the string pointer |
| inc %edi # move to a clear buffer |
| push %rdi # save for later |
| |
| # mov $('['<<8+27),%ax # we want to output ^[[ |
| mov $(0x5b<<8+27),%ax # we want to output ^[[ |
| stosw |
| |
| cdq # clear dx |
| |
| str_loop2: # find end of string |
| inc %edx |
| cmpb $0,(%rcx,%rdx) # repeat till we find zero |
| jne str_loop2 |
| |
| push $81 # one added to cheat, we don't |
| # count the trailing '\n' |
| pop %rax |
| |
| cmp %eax,%edx # see if we are >=80 |
| jl not_too_big # ! if so, don't center |
| push $80 |
| pop %rdx |
| |
| not_too_big: |
| sub %edx,%eax # subtract size from 80 |
| |
| shr %eax # then divide by 2 |
| |
| call num_to_ascii # print number of spaces |
| # mov $'C',%al # tack a 'C' on the end |
| mov $0x43,%al # tack a 'C' on the end |
| # ah is zero from num_to_ascii |
| stosw # store C and a NULL |
| pop %rcx # pop the pointer to ^[[xC |
| |
| call write_stdout # write to the screen |
| |
| done_center: |
| pop %rcx # restore string pointer |
| # and trickily print the real string |
| |
| pop %rdx # restore strcat pointer |
| |
| #================================ |
| # WRITE_STDOUT |
| #================================ |
| # ecx has string |
| # eax,ebx,ecx,edx trashed |
| write_stdout: |
| push %rdx |
| push $SYSCALL_WRITE # put 4 in eax (write syscall) |
| pop %rax # in 3 bytes of code |
| |
| cdq # clear edx |
| |
| lea 1(%rdx),%edi # put 1 in ebx (stdout) |
| # in 3 bytes of code |
| |
| mov %ecx,%esi |
| |
| str_loop1: |
| inc %edx |
| cmpb $0,(%rcx,%rdx) # repeat till zero |
| jne str_loop1 |
| |
| syscall # run the syscall |
| pop %rdx |
| ret |
| |
| ############################## |
| # num_to_ascii |
| ############################## |
| # ax = value to print |
| # edi points to where we want it |
| |
| num_to_ascii: |
| push $10 |
| pop %rbx |
| xor %ecx,%ecx # clear ecx |
| div_by_10: |
| cdq # clear edx |
| div %ebx # divide |
| push %rdx # save for later |
| inc %ecx # add to length counter |
| or %eax,%eax # was Q zero? |
| jnz div_by_10 # ! if not divide again |
| |
| write_out: |
| pop %rax # restore in reverse order |
| add $0x30, %al # convert to ASCII |
| stosb # save digit |
| loop write_out # loop till done |
| ret |
| |
| #=========================================================================== |
| # section .data |
| #=========================================================================== |
| .data |
| |
| ver_string: .ascii " Version \0" |
| compiled_string: .ascii ", Compiled \0" |
| processor: .ascii " Processor\0" |
| s_comma: .ascii "s, \0" |
| ram_comma: .ascii "M RAM, \0" |
| bogo_total: .ascii " Bogomips Total\n\0" |
| |
| default_colors: .ascii "\033[0m\n\n\0" |
| |
| cpuinfo: .ascii "/proc/cpuinfo\0" |
| |
| |
| one: .ascii "One\0\0\0" |
| two: .ascii "Two\0\0\0" |
| three: .ascii "Three\0" |
| four: .ascii "Four\0" |
| |
| .include "logo.lzss_new" |
| |
| disk_buffer: |
| .ascii "processor : 0\n" |
| .ascii "vendor_id : GenuineIntel\n" |
| .ascii "cpu family : 15\n" |
| .ascii "model : 6\n" |
| .ascii "model name : Intel(R) Xeon(TM) CPU 3.46GHz\n" |
| .ascii "stepping : 4\n" |
| .ascii "cpu MHz : 3200.000\n" |
| .ascii "cache size : 2048 KB\n" |
| .ascii "physical id : 0\n" |
| .ascii "siblings : 2\n" |
| .ascii "core id : 0\n" |
| .ascii "cpu cores : 2\n" |
| .ascii "apicid : 0\n" |
| .ascii "initial apicid : 0\n" |
| .ascii "fpu : yes\n" |
| .ascii "fpu_exception : yes\n" |
| .ascii "cpuid level : 6\n" |
| .ascii "wp : yes\n" |
| .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" |
| .ascii "bogomips : 6934.38\n" |
| .ascii "clflush size : 64\n" |
| .ascii "cache_alignment : 128\n" |
| .ascii "address sizes : 36 bits physical, 48 bits virtual\n" |
| .ascii "power management:\n" |
| .ascii "\n" |
| .ascii "processor : 1\n" |
| .ascii "vendor_id : GenuineIntel\n" |
| .ascii "cpu family : 15\n" |
| .ascii "model : 6\n" |
| .ascii "model name : Intel(R) Xeon(TM) CPU 3.46GHz\n" |
| .ascii "stepping : 4\n" |
| .ascii "cpu MHz : 3200.000\n" |
| .ascii "cache size : 2048 KB\n" |
| .ascii "physical id : 1\n" |
| .ascii "siblings : 2\n" |
| .ascii "core id : 0\n" |
| .ascii "cpu cores : 2\n" |
| .ascii "apicid : 4\n" |
| .ascii "initial apicid : 4\n" |
| .ascii "fpu : yes\n" |
| .ascii "fpu_exception : yes\n" |
| .ascii "cpuid level : 6\n" |
| .ascii "wp : yes\n" |
| .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" |
| .ascii "bogomips : 6934.13\n" |
| .ascii "clflush size : 64\n" |
| .ascii "cache_alignment : 128\n" |
| .ascii "address sizes : 36 bits physical, 48 bits virtual\n" |
| .ascii "power management:\n\0" |
| |
| uname_info: |
| .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" |
| .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" |
| |
| .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" |
| .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" |
| |
| .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" |
| .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" |
| |
| .ascii "#1 SMP Mon May 4 09:51:54 EDT 2009\0\0" |
| .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" |
| |
| .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" |
| .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" |
| |
| .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" |
| .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" |
| |
| sysinfo_buff: |
| .long 0,0,0,0,0,0,0,0,2048*1024*1024,0,0,0,0,0,0,0 |
| |
| |
| #============================================================================ |
| # section .bss |
| #============================================================================ |
| .bss |
| |
| .lcomm text_buf, (N+F-1) |
| .lcomm out_buffer,16384 |