1 ;; -----------------------------------------------------------------------
3 ;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
5 ;; This program is free software; you can redistribute it and/or modify
6 ;; it under the terms of the GNU General Public License as published by
7 ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 ;; Boston MA 02111-1307, USA; either version 2 of the License, or
9 ;; (at your option) any later version; incorporated herein by reference.
11 ;; -----------------------------------------------------------------------
16 ;; Common code for running a Linux kernel
20 ; Hook macros, that may or may not be defined
22 %ifndef HAVE_SPECIAL_APPEND
23 %macro SPECIAL_APPEND 0
27 %ifndef HAVE_UNLOAD_PREP
33 ; A Linux kernel consists of three parts: boot sector, setup code, and
34 ; kernel code. The boot sector is never executed when using an external
35 ; booting utility, but it contains some status bytes that are necessary.
37 ; First check that our kernel is at least 1K, or else it isn't long
38 ; enough to have the appropriate headers.
40 ; We used to require the kernel to be 64K or larger, but it has gotten
41 ; popular to use the Linux kernel format for other things, which may
44 ; Additionally, we used to have a test for 8 MB or smaller. Equally
48 push si ; <A> file pointer
51 mov si,KernelCName ; Print kernel name part of
52 call cwritestr ; "Loading" message
56 ; Now start transferring the kernel
58 push word real_mode_seg
62 ; Start by loading the bootsector/setup code, to see if we need to
63 ; do something funky. It should fit in the first 32K (loading 64K won't
64 ; work since we might have funny stuff up near the end of memory).
66 call dot_pause ; Check for abort key
67 mov cx,8000h >> SECTOR_SHIFT ; Half a moby (32K)
69 pop si ; <A> file pointer
73 cmp word [es:bs_bootsign],0AA55h
74 jne kernel_corrupt ; Boot sec signature missing
77 ; Save the file pointer for later...
79 push si ; <A> file pointer
82 ; Construct the command line (append options have already been copied)
86 mov si,boot_image ; BOOT_IMAGE=
89 mov si,KernelCName ; Unmangled kernel name
90 mov cx,[KernelCNameLen]
95 SPECIAL_APPEND ; Module-specific hook
97 mov si,[CmdOptPtr] ; Options from user input
101 ; Scan through the command line for anything that looks like we might be
102 ; interested in. The original version of this code automatically assumed
103 ; the first option was BOOT_IMAGE=, but that is no longer certain.
107 mov [InitRDPtr],ax ; No initrd= option (yet)
108 push es ; Set DS <- real_mode_seg
122 cmp eax,'keep' ; Is it "keeppxe"?
124 cmp dword [si+3],'ppxe'
126 cmp byte [si+7],' ' ; Must be whitespace or EOS
128 or byte [cs:KeepPXE],1
131 push es ; <B> ES -> real_mode_seg
133 pop es ; Set ES <- normal DS
135 mov cx,initrd_cmd_len
141 mov [cs:InitRDPtr],si
145 mov [cs:InitRDPtr],ax
146 .not_initrd: pop es ; <B> ES -> real_mode_seg
147 skip_this_opt: lodsb ; Load from command line
151 jmp short get_next_opt
156 cmp eax,'=nor' ; vga=normal
159 cmp eax,'=ext' ; vga=ext
162 cmp eax,'=ask' ; vga=ask
164 call parseint ; vga=<number>
165 jc skip_this_opt ; Not an integer
166 vc0: mov [bs_vidmode],bx ; Set video mode
167 jmp short skip_this_opt
171 jc skip_this_opt ; Not an integer
172 %if HIGHMEM_SLOP != 0
175 mov [cs:MyHighMemSize],ebx
176 jmp short skip_this_opt
178 push cs ; Restore standard DS
181 mov [CmdLineLen],si ; Length including final null
183 ; Now check if we have a large kernel, which needs to be loaded high
186 mov dword [RamdiskMax], HIGHMEM_MAX ; Default initrd limit
187 cmp dword [es:su_header],HEADER_ID ; New setup code ID
188 jne old_kernel ; Old kernel, load low
189 mov ax,[es:su_version]
190 mov [KernelVersion],ax
191 cmp ax,0200h ; Setup code version 2.0
192 jb old_kernel ; Old kernel, load low
193 cmp ax,0201h ; Version 2.01+?
194 jb new_kernel ; If 2.00, skip this step
195 ; Set up the heap (assuming loading high for now)
196 mov word [es:su_heapend],linux_stack-512
197 or byte [es:su_loadflags],80h ; Let the kernel know we care
198 cmp ax,0203h ; Version 2.03+?
199 jb new_kernel ; Not 2.03+
200 mov eax,[es:su_ramdisk_max]
201 mov [RamdiskMax],eax ; Set the ramdisk limit
204 ; We definitely have a new-style kernel. Let the kernel know who we are,
205 ; and that we are clueful
208 mov byte [es:su_loader],my_id ; Show some ID
210 mov [es:su_ramdisklen],eax ; No initrd loaded yet
213 ; About to load the kernel. This is a modern kernel, so use the boot flags
216 mov al,[es:su_loadflags]
219 ; Cap the ramdisk memory range if appropriate
221 cmp eax,[MyHighMemSize]
223 mov [MyHighMemSize],eax
229 ; Load the kernel. We always load it at 100000h even if we're supposed to
230 ; load it "low"; for a "low" load we copy it down to low memory right before
234 movzx ax,byte [es:bs_setupsecs] ; Setup sectors
237 mov al,4 ; 0 = 4 setup sectors
239 inc ax ; Including the boot sector
245 ; Move the stuff beyond the setup code to high memory at 100000h
247 movzx esi,word [SetupSecs] ; Setup sectors
248 shl si,9 ; Convert to bytes
250 sub ecx,esi ; Number of bytes to copy
251 add esi,(real_mode_seg << 4) ; Pointer to source
252 mov edi,100000h ; Copy to address 100000h
254 call bcopy ; Transfer to high memory
256 pop si ; <A> File pointer
257 and si,si ; EOF already?
260 ; On exit EDI -> where to load the rest
263 or eax,-1 ; Load the whole file
264 mov dx,3 ; Pad to dword
269 mov ax,real_mode_seg ; Set to real mode seg
275 ; Some older kernels (1.2 era) would have more than 4 setup sectors, but
276 ; would not rely on the boot protocol to manage that. These kernels fail
277 ; if they see protected-mode kernel data after the setup sectors, so
289 ; Now see if we have an initial RAMdisk; if so, do requisite computation
290 ; We know we have a new kernel; the old_kernel code already will have objected
291 ; if we tried to load initrd using an old kernel
297 call parse_load_initrd
301 ; Abandon hope, ye that enter here! We do no longer permit aborts.
303 call abort_check ; Last chance!!
308 UNLOAD_PREP ; Module-specific hook
311 ; Now, if we were supposed to load "low", copy the kernel down to 10000h
312 ; and the real mode stuff to 90000h. We assume that all bzImage kernels are
313 ; capable of starting their setup from a different address.
320 ; If the default root device is set to FLOPPY (0000h), change to
323 cmp word [es:bs_rootdev],byte 0
325 mov word [es:bs_rootdev],0200h
329 ; Copy command line. Unfortunately, the old kernel boot protocol requires
330 ; the command line to exist in the 9xxxxh range even if the rest of the
334 mov dx,[KernelVersion]
335 test byte [LoadFlags],LOAD_HIGH
336 jz .need_high_cmdline
337 cmp dx,0202h ; Support new cmdline protocol?
338 jb .need_high_cmdline
339 ; New cmdline protocol
340 ; Store 32-bit (flat) pointer to command line
341 ; This is the "high" location, since we have bzImage
342 mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4)+cmd_line_here
343 mov word [HeapEnd],linux_stack
344 mov word [fs:su_heapend],linux_stack-512
349 ; Copy command line down to fit in high conventional memory
350 ; -- this happens if we have a zImage kernel or the protocol
354 mov di,old_cmd_line_here
355 mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
356 mov [fs:kern_cmd_offset],di ; Store pointer
357 mov word [HeapEnd],old_linux_stack
358 mov ax,255 ; Max cmdline limit
362 mov word [fs:su_heapend],old_linux_stack-512
365 ; Note that the only reason we would end up here is
366 ; because we have a zImage, so we anticipate the move
367 ; to 90000h already...
368 mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here
369 mov ax,old_max_cmd_len ; 2.02+ allow a higher limit
375 mov cx,ax ; Truncate the command line
378 stosb ; Final null, note AL=0 already
382 mov [es:su_movesize],di ; Tell the kernel what to move
387 ; Time to start setting up move descriptors
391 xor cx,cx ; Number of descriptors
393 mov bx,es ; real_mode_seg
395 push ds ; We need DS == ES == CS here
398 test byte [LoadFlags],LOAD_HIGH
401 ; Loading low: move real_mode stuff to 90000h, then move the kernel down
404 mov eax,real_mode_seg << 4
406 movzx eax,word [CmdLineEnd]
410 mov eax,10000h ; Target address of low kernel
412 mov eax,100000h ; Where currently loaded
419 mov bx,9000h ; Revised real mode segment
423 cmp word [InitRDPtr],0 ; Did we have an initrd?
426 mov eax,[fs:su_ramdiskat]
428 mov eax,[InitRDStart]
430 mov eax,[fs:su_ramdisklen]
435 push cx ; Length of descriptor list
457 mov dword [EntryPoint],run_linux_kernel
458 ; BX points to the final real mode segment, and will be loaded
460 jmp replace_bootstrap
465 ; Set up segment registers and the Linux real-mode stack
466 ; Note: ds == the real mode segment
471 mov sp,strict word linux_stack
472 ; Point HeapEnd to the immediate of the instruction above
473 HeapEnd equ $-2 ; Self-modifying code! Fun!
479 ; We're done... now RUN THAT KERNEL!!!!
480 ; Setup segment == real mode segment + 020h; we need to jump to offset
481 ; zero in the real mode segment.
489 ; Load an older kernel. Older kernels always have 4 setup sectors, can't have
490 ; initrd, and are always loaded low.
494 cmp word [InitRDPtr],ax ; Old kernel can't have initrd
499 mov byte [LoadFlags],al ; Always low
500 mov word [KernelVersion],ax ; Version 0.00
506 ; Parse an initrd= option and load the initrds. This sets
507 ; InitRDStart and InitRDEnd with dword padding between; we then
508 ; do a global memory shuffle to move it to the end of memory.
510 ; On entry, EDI points to where to start loading.
518 pop es ; DS == real_mode_seg, ES == CS
520 mov [cs:InitRDStart],edi
521 mov [cs:InitRDEnd],edi
523 mov si,[cs:InitRDPtr]
526 ; DS:SI points to the start of a name
538 push ax ; Terminating character
539 push si ; Next filename (if any)
540 mov byte [si-1],0 ; Zero-terminate
541 mov si,bx ; Current filename
544 mov di,InitRD ; Target buffer for mangled name
551 mov [si-1],al ; Restore ending byte
556 ; Compute the initrd target location
557 mov edx,[cs:InitRDEnd]
558 sub edx,[cs:InitRDStart]
559 mov [su_ramdisklen],edx
560 mov eax,[cs:MyHighMemSize]
562 and ax,0F000h ; Round to a page boundary
563 mov [su_ramdiskat],eax
570 ; Load RAM disk into high memory
572 ; Input: InitRD - set to the mangled name of the initrd
573 ; EDI - location to load
574 ; Output: EDI - location for next initrd
575 ; InitRDEnd - updated
580 mov ax,cs ; CS == DS == ES
586 call unmangle_name ; Create human-readable name
588 mov [InitRDCNameLen],di
590 call searchdir ; Look for it in directory
595 mov si,crlfloading_msg ; Write "Loading "
597 mov si,InitRDCName ; Write ramdisk name
599 mov si,dotdot_msg ; Write dots
620 no_high_mem: ; Error routine
627 crlfloading_msg db CR, LF
628 loading_msg db 'Loading ', 0
631 ready_msg db 'ready.', CR, LF, 0
632 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
634 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
636 boot_image db 'BOOT_IMAGE='
637 boot_image_len equ $-boot_image
641 MyHighMemSize resd 1 ; Possibly adjusted highmem size
642 RamdiskMax resd 1 ; Highest address for ramdisk
643 KernelSize resd 1 ; Size of kernel in bytes
644 KernelSects resd 1 ; Size of kernel in sectors
645 KernelEnd resd 1 ; Ending address of the kernel image
646 InitRDStart resd 1 ; Start of initrd (pre-relocation)
647 InitRDEnd resd 1 ; End of initrd (pre-relocation)
648 CmdLineLen resw 1 ; Length of command line including null
649 CmdLineEnd resw 1 ; End of the command line in real_mode_seg
650 SetupSecs resw 1 ; Number of setup sectors (+bootsect)
651 InitRDPtr resw 1 ; Pointer to initrd= option in command line
652 KernelVersion resw 1 ; Kernel protocol version
653 LoadFlags resb 1 ; Loadflags from kernel