# # linux_logo in x86_64 assembly language # based on the code from ll_asm-0.36 # # By Vince Weaver # # 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 # 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