Home | History | Annotate | Download | only in x86-linux
      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