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
50 cmp ax,1024 ; Bootsect + 1 setup sect
58 ; Now start transferring the kernel
60 push word real_mode_seg
63 movzx eax,ax ; Fix this by using a 32-bit
64 shl edx,16 ; register for the kernel size
69 mov [KernelSects],eax ; Total sectors in kernel
72 ; Now, if we transfer these straight, we'll hit 64K boundaries. Hence we
73 ; have to see if we're loading more than 64K, and if so, load it step by
78 ; Start by loading the bootsector/setup code, to see if we need to
79 ; do something funky. It should fit in the first 32K (loading 64K won't
80 ; work since we might have funny stuff up near the end of memory).
81 ; If we have larger than 32K clusters, yes, we're hosed.
83 call abort_check ; Check for abort key
84 mov ecx,8000h >> SECTOR_SHIFT ; Half a moby (32K)
91 pop si ; Cluster pointer on stack
93 cmp word [es:bs_bootsign],0AA55h
94 jne kernel_corrupt ; Boot sec signature missing
97 ; Save the cluster pointer for later...
102 ; Initialize our end of memory pointer
104 mov eax,[HighMemRsvd]
105 xor ax,ax ; Align to a 64K boundary
106 mov [MyHighMemSize],eax
109 ; Construct the command line (append options have already been copied)
113 mov si,boot_image ; BOOT_IMAGE=
114 mov cx,boot_image_len
116 mov si,KernelCName ; Unmangled kernel name
117 mov cx,[KernelCNameLen]
122 SPECIAL_APPEND ; Module-specific hook
124 mov si,[CmdOptPtr] ; Options from user input
128 ; Scan through the command line for anything that looks like we might be
129 ; interested in. The original version of this code automatically assumed
130 ; the first option was BOOT_IMAGE=, but that is no longer certain.
134 mov [InitRDPtr],ax ; No initrd= option (yet)
135 push es ; Set DS <- real_mode_seg
149 cmp eax,'keep' ; Is it "keeppxe"?
151 cmp dword [si+3],'ppxe'
153 cmp byte [si+7],' ' ; Must be whitespace or EOS
155 or byte [cs:KeepPXE],1
158 push es ; Save ES -> real_mode_seg
160 pop es ; Set ES <- normal DS
162 mov cx,initrd_cmd_len
168 mov [cs:InitRDPtr],si
172 mov [cs:InitRDPtr],ax
173 .not_initrd: pop es ; Restore ES -> real_mode_seg
174 skip_this_opt: lodsb ; Load from command line
178 jmp short get_next_opt
183 cmp eax,'=nor' ; vga=normal
186 cmp eax,'=ext' ; vga=ext
189 cmp eax,'=ask' ; vga=ask
191 call parseint ; vga=<number>
192 jc skip_this_opt ; Not an integer
193 vc0: mov [bs_vidmode],bx ; Set video mode
194 jmp short skip_this_opt
198 jc skip_this_opt ; Not an integer
199 %if HIGHMEM_SLOP != 0
202 mov [cs:MyHighMemSize],ebx
203 jmp short skip_this_opt
205 push cs ; Restore standard DS
208 mov [CmdLineLen],si ; Length including final null
210 ; Now check if we have a large kernel, which needs to be loaded high
213 mov dword [RamdiskMax], HIGHMEM_MAX ; Default initrd limit
214 cmp dword [es:su_header],HEADER_ID ; New setup code ID
215 jne old_kernel ; Old kernel, load low
216 mov ax,[es:su_version]
217 mov [KernelVersion],ax
218 cmp ax,0200h ; Setup code version 2.0
219 jb old_kernel ; Old kernel, load low
220 cmp ax,0201h ; Version 2.01+?
221 jb new_kernel ; If 2.00, skip this step
222 ; Set up the heap (assuming loading high for now)
223 mov word [es:su_heapend],linux_stack-512
224 or byte [es:su_loadflags],80h ; Let the kernel know we care
225 cmp ax,0203h ; Version 2.03+?
226 jb new_kernel ; Not 2.03+
227 mov eax,[es:su_ramdisk_max]
228 mov [RamdiskMax],eax ; Set the ramdisk limit
231 ; We definitely have a new-style kernel. Let the kernel know who we are,
232 ; and that we are clueful
235 mov byte [es:su_loader],my_id ; Show some ID
237 mov [es:su_ramdisklen],eax ; No initrd loaded yet
240 ; About to load the kernel. This is a modern kernel, so use the boot flags
243 mov al,[es:su_loadflags]
246 ; Load the kernel. We always load it at 100000h even if we're supposed to
247 ; load it "low"; for a "low" load we copy it down to low memory right before
251 movzx ax,byte [es:bs_setupsecs] ; Setup sectors
254 mov al,4 ; 0 = 4 setup sectors
258 mov si,KernelCName ; Print kernel name part of
259 call cwritestr ; "Loading" message
260 mov si,dotdot_msg ; Print dots
263 mov eax,[MyHighMemSize]
264 sub eax,100000h ; Load address
266 jb no_high_mem ; Not enough high memory
268 ; Move the stuff beyond the setup code to high memory at 100000h
270 movzx esi,word [SetupSecs] ; Setup sectors
271 inc si ; plus 1 boot sector
272 shl si,9 ; Convert to bytes
274 sub ecx,esi ; Number of bytes to copy
276 add esi,(real_mode_seg << 4) ; Pointer to source
277 mov edi,100000h ; Copy to address 100000h
279 call bcopy ; Transfer to high memory
281 ; On exit EDI -> where to load the rest
283 mov si,dot_msg ; Progress report
287 pop ecx ; Number of bytes in the initial portion
288 pop si ; Restore file handle/cluster pointer
290 sub eax,8000h ; Amount of kernel not yet loaded
291 jbe high_load_done ; Zero left (tiny kernel)
293 xor dx,dx ; No padding needed
294 mov bx,dot_pause ; Print dots...
295 call load_high ; Copy the file
299 mov ax,real_mode_seg ; Set to real mode seg
306 ; Now see if we have an initial RAMdisk; if so, do requisite computation
307 ; We know we have a new kernel; the old_kernel code already will have objected
308 ; if we tried to load initrd using an old kernel
311 cmp word [InitRDPtr],0
313 call parse_load_initrd
316 ; Abandon hope, ye that enter here! We do no longer permit aborts.
318 call abort_check ; Last chance!!
323 call vgaclearmode ; We can't trust ourselves after this
325 UNLOAD_PREP ; Module-specific hook
328 ; Now, if we were supposed to load "low", copy the kernel down to 10000h
329 ; and the real mode stuff to 90000h. We assume that all bzImage kernels are
330 ; capable of starting their setup from a different address.
337 ; Copy command line. Unfortunately, the old kernel boot protocol requires
338 ; the command line to exist in the 9xxxxh range even if the rest of the
342 cli ; In case of hooked interrupts
343 mov dx,[KernelVersion]
344 test byte [LoadFlags],LOAD_HIGH
346 cmp dx,0202h ; Support new cmdline protocol?
348 ; New cmdline protocol
349 ; Store 32-bit (flat) pointer to command line
350 ; This is the "high" location, since we have bzImage
351 mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4)+cmd_line_here
356 ; Copy command line down to fit in high conventional memory
357 ; -- this happens if we have a zImage kernel or the protocol
361 mov di,old_cmd_line_here
362 mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
363 mov [fs:kern_cmd_offset],di ; Store pointer
364 mov word [HeapEnd],old_linux_stack
365 mov ax,255 ; Max cmdline limit
369 mov word [fs:su_heapend],old_linux_stack-512
372 ; Note that the only reason we would end up here is
373 ; because we have a zImage, so we anticipate the move
374 ; to 90000h already...
375 mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here
376 mov ax,4095 ; 2.02+ allow a higher limit
382 mov cx,ax ; Truncate the command line
385 stosb ; Final null, note AL=0 already
388 mov [es:su_movesize],di ; Tell the kernel what to move
391 test byte [LoadFlags],LOAD_HIGH
392 jnz in_proper_place ; If high load, we're done
395 ; Loading low; we can't assume it's safe to run in place.
397 ; Copy real_mode stuff up to 90000h
401 mov cx,di ; == su_movesize (from above)
403 shr cx,2 ; Convert to dwords
406 fs rep movsd ; Copy setup + boot sector
408 ; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
409 ; setup sectors, but the boot protocol had not yet been defined. They
410 ; rely on a signature to figure out if they need to copy stuff from
411 ; the "protected mode" kernel area. Unfortunately, we used that area
412 ; as a transfer buffer, so it's going to find the signature there.
413 ; Hence, zero the low 32K beyond the setup area.
416 inc di ; Setup + boot sector
417 mov cx,32768/512 ; Sectors/32K
418 sub cx,di ; Remaining sectors
419 shl di,9 ; Sectors -> bytes
420 shl cx,7 ; Sectors -> dwords
422 rep stosd ; Clear region
424 ; Copy the kernel down to the "low" location (the kernel will then
425 ; move itself again, sigh.)
433 ; Now everything is where it needs to be...
435 ; When we get here, es points to the final segment, either
436 ; 9000h or real_mode_seg
441 ; If the default root device is set to FLOPPY (0000h), change to
444 cmp word [es:bs_rootdev],byte 0
446 mov word [es:bs_rootdev],0200h
450 ; Copy the disk table to high memory, then re-initialize the floppy
453 %if IS_SYSLINUX || IS_MDSLINUX
458 mov [cs:fdctab],word linux_fdctab ; Save new floppy tab pos
462 call cleanup_hardware
464 ; If we're debugging, wait for a keypress so we can read any debug messages
471 ; Set up segment registers and the Linux real-mode stack
472 ; Note: es == the real mode segment
481 mov sp,strict word linux_stack
482 ; Point HeapEnd to the immediate of the instruction above
483 HeapEnd equ $-2 ; Self-modifying code! Fun!
486 ; We're done... now RUN THAT KERNEL!!!!
487 ; Setup segment == real mode segment + 020h; we need to jump to offset
488 ; zero in the real mode segment.
496 ; Load an older kernel. Older kernels always have 4 setup sectors, can't have
497 ; initrd, and are always loaded low.
501 cmp word [InitRDPtr],ax ; Old kernel can't have initrd
506 mov byte [LoadFlags],al ; Always low
507 mov word [KernelVersion],ax ; Version 0.00
513 ; Parse an initrd= option and load the initrds. Note that we load
514 ; from the high end of memory first, so we parse this option from
523 pop es ; DS == real_mode_seg, ES == CS
525 mov si,[cs:InitRDPtr]
530 ; Now SI points to one character beyond the
531 ; byte that ended this option.
536 ; DS:SI points to a termination byte
539 xchg al,[si] ; Zero-terminate
540 push si ; Save ending byte address
541 push ax ; Save ending byte
545 cmp si,[cs:InitRDPtr]
555 mov di,InitRD ; Target buffer for mangled name
562 mov [di],al ; Restore ending byte
564 cmp si,[cs:InitRDPtr]
572 ; Load RAM disk into high memory
574 ; Input: InitRD - set to the mangled name of the initrd
579 mov ax,cs ; CS == DS == ES
584 call unmangle_name ; Create human-readable name
586 mov [InitRDCNameLen],di
588 call searchdir ; Look for it in directory
593 mov cx,ax ; ECX <- ram disk length
598 push ecx ; Bytes to load
599 mov edx,[MyHighMemSize] ; End of memory
601 mov eax,[RamdiskMax] ; Highest address allowed by kernel
604 mov edx,eax ; Adjust to fit inside limit
607 and dx,0F000h ; Round down to 4K boundary
608 sub edx,ecx ; Subtract size of ramdisk
609 and dx,0F000h ; Round down to 4K boundary
610 cmp edx,[KernelEnd] ; Are we hitting the kernel image?
613 cmp dword [es:su_ramdisklen],0
615 ; The total length has to include the padding between
616 ; different ramdisk files, so consider "the length" the
617 ; total amount we're about to adjust the base pointer.
618 mov ecx,[es:su_ramdiskat]
621 add [es:su_ramdisklen],ecx
623 mov [es:su_ramdiskat],edx ; Load address
624 mov edi,edx ; initrd load address
626 dec edx ; Note: RamdiskMax is addr-1
627 mov [RamdiskMax],edx ; Next initrd loaded here
630 mov si,crlfloading_msg ; Write "Loading "
632 mov si,InitRDCName ; Write ramdisk name
634 mov si,dotdot_msg ; Write dots
638 pop eax ; Bytes to load
639 mov dx,0FFFh ; Pad to page
640 mov bx,dot_pause ; Print dots...
641 call load_high ; Load the file
655 no_high_mem: ; Error routine
662 crlfloading_msg db CR, LF
663 loading_msg db 'Loading ', 0
666 ready_msg db 'ready.', CR, LF, 0
667 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
669 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
670 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
672 boot_image db 'BOOT_IMAGE='
673 boot_image_len equ $-boot_image
677 MyHighMemSize resd 1 ; Possibly adjusted highmem size
678 RamdiskMax resd 1 ; Highest address for ramdisk
679 KernelSize resd 1 ; Size of kernel in bytes
680 KernelSects resd 1 ; Size of kernel in sectors
681 KernelEnd resd 1 ; Ending address of the kernel image
682 CmdLineLen resw 1 ; Length of command line including null
683 SetupSecs resw 1 ; Number of setup sectors
684 InitRDPtr resw 1 ; Pointer to initrd= option in command line
685 KernelVersion resw 1 ; Kernel protocol version
686 LoadFlags resb 1 ; Loadflags from kernel