blob: 8f446063b6b3458fceb3a61bd131273e76bf3572 [file] [log] [blame]
#
# 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