! Linux kernel setup for WYSE Winterm devices (at least WT3320SE) ! Based on setup code for AMD Élan devices by Anders Larsen ! ! Winterm version Copyright (C) 2005 by Wilmer van der Gaast ! ! Portions are Copyright (C) 2000 by Denis Hatebur and TueViT Essen ! Portions are Copyright (C) 2000 by Anders Larsen and Baumer Ident GmbH ! ! Derived from setup.S Copyright (C) 1991-1997 Linus Torvalds et al. ! ! This program is free software; you can redistribute it and/or modify ! it under the terms of the GNU General Public License as published by ! the Free Software Foundation; either version 2 of the License, or ! (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! You should have received a copy of the GNU General Public License ! along with this program. If not, write to the Free Software ! Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include .org 0x0 .text USE32 entry CODE1 .org 0x1e0 ; Note: This is the size of *extended* memory, i.e. the memory above ; the 1M mark! mem_size: .long 0 .org 0x1f1 .byte 0 ; setup_sects .word 0 ; read-only rootfs? .long 0 ; syssize (not necessary in protocol 2.02) .word 0 ; ram_size (don't use!) .word 0xffff ; vid_mode .word 0x0100 ; root_dev .word 0xaa55 ; magic! start: .word 0 ! ------------------------ start of header -------------------------------- ! ! SETUP-header, must start at CS:2 (old 0x9020:2) ! .ascii "HdrS" ! Signature for SETUP-header .word 0x0202 ! Version number of header format ! (must be >= 0x0105 ! else old loadlin-1.5 will fail) realmode_swtch: .word 0,0 ! default_switch,SETUPSEG start_sys_seg: .word 0x1000 .word kernel_version !pointing to kernel version string ! note: above part of header is compatible with loadlin-1.5 (header v1.5), ! must not change it type_of_loader: .byte 0xff ! = 0, old one (LILO, Loadlin, ! Bootlin, SYSLX, bootsect...) ! else it is set by the loader: ! 0xTV: T=0 for LILO ! T=1 for Loadlin ! T=2 for bootsect-loader ! T=3 for SYSLX ! T=4 for ETHERBOOT ! V = version wloadflags: ! flags, unused bits must be zero (RFU) .byte 0x01 setup_move_size: .word 0x8000 ! size to move, when we (setup) are not ! loaded at 0x90000. We will move ourselves ! to 0x90000 then just before jumping into ! the kernel. However, only the loader ! know how much of data behind us also needs ! to be loaded. code32_start: ! here loaders can put a different .long 0x100000 ! 0x100000 = default for big kernel ramdisk_image: .long 0 ! address of loaded ramdisk image ! Here the loader (or kernel generator) puts ! the 32-bit address were it loaded the image. ! This only will be interpreted by the kernel. ramdisk_size: .long 0 ! its size in bytes bootsect_kludge: .word 0,0 heap_end_ptr: .word 0x9010+eof/16 ! space from here (exclusive) down to ! end of setup code can be used by setup ! for local heap purposes. ! ------------------------ end of header ---------------------------------- .org 0x1000 start_of_setup: CODE1: cli ! Disable Interrupts call nl_out mov al, #'W call ch_out mov al, #'I call ch_out mov al, #0x80 out #0x70, al ! Disable NMIs mov al, #'L call ch_out call ch_out mov al, #'O call ch_out call nl_out mov esi, kernel_version+0x90000 call str_out call nl_out mov esi, params_head+0x90000 call str_out mov esi, kernelparam+0x90000 call str_out call nl_out mov eax, kernelparam+0x90000 mov [0x90228], eax ; Only do memory detection (and initrd move) if it's not specified yet mov eax, [mem_size+0x90000] or eax, eax jz do_mem_detect ; Hurray for indirect jumps... mov ecx, skip_mem_detect+0x90000 jmp ecx do_mem_detect: mov esi, memcount_msg+0x90000 call str_out ; Disable memory caching for the memory test, it might spoil the ; results mov eax, cr0 or eax, 0x40000000 mov cr0, eax wbinvd ; Start testing memory at the 8MB mark, so we assume there is at ; least that much (most Winterms have at least 32MB anyway) mov edi, 8*1048576-4 memsize_loop: mov eax, [edi] xor eax, 0x55555555 ; This part is kind of fragile, mov [edi], eax && xor [edi], 0x55555555 ; cmp [edi], eax always returns ZF, even if [edi] cmp eax, [edi] ; is not memory, for example. This works though. jne memsize_found xor [edi], 0x55555555 ; Restore memory, there might be an initrd here. add edi, 65536 jmp memsize_loop memsize_found: sub edi, 65532 mov eax, edi call hex_out ; Kernel wants this value in 1K blocks, and not counting "conventional" ; memory. shr eax, 10 sub eax, 1024 mov [mem_size+0x90000], eax ; nl_out touches al, so don't do this earlier call nl_out ; Turn memory cache back on, or the rest will be sloooooow :-) mov eax, cr0 and eax, 0x9fffffff mov cr0, eax ; Now let's see, do we have a RAM-disk? If so, we should try to keep ; it as high in memory as possible. Don't start at the first byte with ; moving, this would make a mess if there's an overlap. ; We'll go backwards. cmp dword [ramdisk_image+0x90000], 0 jz skip_mem_detect mov esi, rdmove_msg+0x90000 call str_out mov esi, [ramdisk_image+0x90000] add esi, [ramdisk_size+0x90000] sub esi, 4 ; edi still contains the memory size in bytes! sub edi, [ramdisk_size+0x90000] and edi, 0xfffe0000 mov [ramdisk_image+0x90000], edi ; Save the position now already add edi, [ramdisk_size+0x90000] sub edi, 4 ; Count the number of DWORDS we have to move mov ecx, [ramdisk_size+0x90000] add ecx, 3 shr ecx, 2 std rep movsd cld ; Dump the new position. Not too useful, but nice for debugging. mov eax, [ramdisk_image+0x90000] call hex_out call nl_out skip_mem_detect: ; Old place for memory size, don't know if any recent kernel really ; cares, but just to be sure.. mov eax, [mem_size+0x90000] mov [0x90002], ax ; Set up IDT/GDTs that are useful for the kernel seg ds lidt [idt_48+0x90000] ! load idt with 0,0 seg ds lgdt [gdt_48+0x90000] ! load gdt with whatever appropriate ! make sure any possible coprocessor is properly reset.. xor ax,ax out #0xf0,al call delay out #0xf1,al call delay ! well, that went ok, I hope. Now we have to reprogram the interrupts :-( ! we put them right after the intel-reserved hardware interrupts, at ! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really ! messed this up with the original PC, and they haven't been able to ! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, ! which is used for the internal hardware interrupts as well. We just ! have to reprogram the 8259's, and it isn't fun. mov al,#0x11 ! initialization sequence out #0x20,al ! send it to 8259A-1 call delay out #0xA0,al ! and to 8259A-2 call delay mov al,#0x20 ! start of hardware int's (0x20) out #0x21,al call delay mov al,#0x28 ! start of hardware int's 2 (0x28) out #0xA1,al call delay mov al,#0x04 ! 8259-1 is master out #0x21,al call delay mov al,#0x02 ! 8259-2 is slave out #0xA1,al call delay mov al,#0x01 ! 8086 mode for both out #0x21,al call delay out #0xA1,al call delay mov al,#0xFF ! mask off all interrupts for now out #0xA1,al call delay mov al,#0xFB ! mask all irq's but irq2 which out #0x21,al ! is cascaded mov esi, start_linux+0x90000 call str_out mov bx, #0 mov esi, #0x90000 ; JMP NEW_CS:00100000 db 0xea dd 0x100000 dw 0x10 ! ! Delay is needed after doing I/O ! delay: .word 0x00eb ! jmp $+2 ret !****************************************************** ! Outputs Character: AL -> COM1 !****************************************************** ch_out: ! ret push eax mov eax, #0x00008000 loop3: sub eax, #0x1 jnz loop3 mov dx, #0x3f8+0x5 ch_w: in al, dx and al, #0x40 jz ch_w pop eax ch_outw: mov dx, #0x3f8 out dx, al ! serial out .... only try ret !****************************************************** ! Outputs a \r\n !****************************************************** nl_out: mov al, 0x0d call ch_out mov al, 0x0a call ch_out ret !****************************************************** ! Outputs a String: [ESI] -> COM1 !****************************************************** str_out: lodsb or al, al jz str_out_end call ch_out jmp str_out str_out_end: ret !****************************************************** ! Outputs a Hex number: EAX -> COM !****************************************************** hex_out: mov ecx, 8 ho_loop: rol eax, #4 push ax and al, #0x0f add al, #'0 cmp al, #'9 jbe ho_1 add al, #7 ho_1: call ch_out pop ax loop ho_loop push ax mov al, #0x20 call ch_out pop ax ret !****************************************************** !**** D A T A ***************************************** !****************************************************** gdt_len: dw 0 gdt_base: dd 0 kernel_version: .ascii "Linux " .ascii " " ; version goes here .byte 0x0 start_linux: .word 0x0a0d .ascii "Starting Linux..." .word 0x0a0d .byte 0x0 params_head: .ascii "Kernel parameters: " .byte 0x0 memcount_msg: .ascii "Counting memory... " .byte 0x0 rdmove_msg: .ascii "Moving initrd... " .byte 0x0 .align 16 gdt: .word 0,0,0,0 .word 0,0,0,0 .word 0xFFFF ! 4Gb - (0x100000*0x1000 = 4Gb) .word 0x0000 ! base address=0 .word 0x9A00 ! code read/exec .word 0x00CF ! granularity=4096, 386 (+5th nibble of limit) .word 0xFFFF ! 4Gb - (0x100000*0x1000 = 4Gb) .word 0x0000 ! base address=0 .word 0x9200 ! data read/write .word 0x00CF ! granularity=4096, 386 (+5th nibble of limit) gdt_end: .word 0 idt_48: .word 0 ! idt limit=0 .long 0 ! idt base=0L gdt_48: .word gdt_end-gdt ! .long 0x90000+gdt ! gdt base = 0X9xxxx .byte 0x0 kernelparam: ;.ascii "console=ttyS0,38400N8" ; .ascii " root=/dev/ram0 rw " .ascii " root=/dev/sda1 lang=us quiet tz=Europe/Amsterdam" .ascii " video=nscfb:vmode:1024x768-16,vfreq:75 splash=silent" .ascii " reboot=bios" .byte 0x0 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! setup_sig1: .word 0xAA55 setup_sig2: .word 0x5A5A eof: