diff -uprN -X linux-2.6.20/Documentation/dontdiff linux-2.6.20/arch/i386/kernel/realmode.c linux-2.6.20-gujin/arch/i386/kernel/realmode.c --- linux-2.6.20/arch/i386/kernel/realmode.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20-gujin/arch/i386/kernel/realmode.c 2007-02-05 21:27:01.000000000 +0000 @@ -0,0 +1,1997 @@ +/* + * This file "realmode.c" is licensed with the same license as the Linux + * kernel; anyway the mapping of the data structure produced is owned by + * Linux kernel copyright owner(s) for a long time. + * + * The main use of this file is to produce the real-mode part of the + * ELF file vmlinux (at the root of the linux tree), this file will + * be (usually) stripped of debug info and then compressed by GZIP + * to make a "Gujin native bootable file" with the "kgz" extension. + * This real-mode part is removed (by objdump) from the binary later + * in the process if you generate a bzImage file. + * This real-mode part is used at boot time to get informations from + * the BIOS: memory size, disk present, video mode... and is no more + * accessible once the processor has switch to protected-mode. + * + * Gujin homepage is at: http://gujin.org/ ; it is itself licensed + * under the terms of the GNU General Public License, version 2. + * Gujin is Copyright (C) 1999-2005 Etienne Lorrain, fingerprint (2D3AF3EA): + * 2471 DF64 9DEE 41D4 C8DB 9667 E448 FF8C 2D3A F3EA + * E-Mail: etienne dot lorrain at gujin dot org + * + * First version has been written by Etienne Lorrain in December 2004. + */ + +/* + * Do not touch - used to generate real mode ia32 code and manage + * the inter-segment call transparently, after maximum 4 Kbytes + * of real mode rodata/data has been copied into %ds segment. + * + * Having constant/initialised variable in here is a bit tricky, + * it uses the fact that since Gujin-1.0, the segment ".fourKsegment" + * containning the only variable "fourKbuffer" (of size 4 Kbytes) + * is located at %ds:0 i.e. before the ".rodata" of Gujin. + * Note that here the code segment is local but the data segment + * (and the stack segment) is the one of Gujin for obvious reasons. + * The first person modifying data at address over %ds:0x1000 + * get all the blame for any bug happening in Gujin, in between + * the last and the next big bang, and it is a pretty long time. + * You don't like my memory protection system? + */ +asm(" .psize 0 \n" + " .code16gcc \n" + " .section .init.text16,\"ax\",@progbits \n" + " push %di \n" + " push %si \n" + " mov $_sdata16,%si \n" + " mov %si,%di \n" + " mov $_edata16,%cx \n" + " and $0xFFF,%cx \n" + " sub %si,%cx \n" + " shr $2,%cx \n" + " rep movsl %cs:(%si),%es:(%di) \n" + " pop %si \n" + " pop %di \n" + " calll linux_set_params \n" + " lretw \n" + " .previous \n" +); + +#include "asm/realmode.h" + +/* + * Those need to be used because there is no way to set the default + * segment name used for ".text", ".data", ".rodata*" and ".bss" + * It would be nice to be able to do: asm(".equ .data, .data16"); + * or shall that be managed by the compiler - and by compilation + * switch or by the attribute of functions? + * This file is pre-linked (section with same name grouped together) + * in "arch/i386/kernel/built-in.o" so no hope to manage it in + * the final linker file if it uses the same section names. + * Maybe if "arch/i386/kernel/built-in.o" could be an archive? + * Maybe it is "the right way (TM)" to use different section names + * so that those functions can be located in different source files. + * Maybe the section name shall be automatically changed using + * objcopy in the realmode.o file, just after compilation. + * Maybe the segment names shall be changed by sed in the + * assembler file. + * The method used here works and is as fool proof as possible. + * + * Unfortunately, I do not know how to use inline strings here, + * i.e. puts("hello world"); will not work due to the segment + * used to store the constant "hello world". + * Same for the implicit table generated by a big switch(). + * This would generate a prohibited cross reference at linker time. + * + * I also need to add that using GCC to generate real-mode i386 + * executable has a major limitation: no use of library compiled + * for protected mode. If your source make GCC to call memcpy(), + * memset(), strcpy()... then you have to provide them yourself. + * The same thing apply if you multiply or divide 64 bit numbers + * by non power of two. Adding and substracting of long long is OK. + */ +#define CONST __attribute__ ((section (".rodata16"))) const +#define VARIABLE __attribute__ ((section (".data16"))) +#define BSS __attribute__ ((section (".bss16"))) +#define CODE __attribute__ ((section (".text16"))) + +#if 0 +/* + * When compiling this file, some combination of GCC and options + * will call memset() in the assembly generated. You can check it + * by typing "make arch/i386/kernel/realmode.s". + * They cannot be inline because they are _called_ by the compiler. + * It is not possible to access the standard one because its + * hexadecimal coding is for protected mode, and not possible to + * code it in assembler because of we do not know the calling + * convension (GCC optimisations like "-mtrd", "-mregparm=3"). + * We also cannot have two memcpy() nor memset() in the same + * final link, so we rename them in assembler. + * This part is currently disabled due to a problem in the linker + * but right now no GCC switch combination produces such + * memset/memcpy calls with a Linux supported compiler. + */ +CODE void code16_memcpy (char *dest, const char *src, unsigned nb) +{ + while (nb--) + *dest++ = *src++; +} +CODE void code16_memset (char *dest, unsigned val, unsigned nb) +{ + while (nb--) + *dest++ = val; +} +asm ( + "memcpy = code16_memcpy \n" + "memset = code16_memset \n" +); +#endif + +/** + ** Few debug functions and how to use them, including how to declare + ** variables and constants. For complex debug, you may want to + ** check dbgload.exe of Gujin. The following debug uses the VIDEO BIOS + ** to display messages so will not work in VESA modes when the VESA + ** BIOS do not support printing characters in current video mode + ** (bit BIOS_output_supported of VESA_modeinfo_t / struct screen_info) + **/ +//#define TEST_VGAPRINT + +#ifdef TEST_VGAPRINT +CONST char test_before[] = "test: 0x"; +VARIABLE unsigned test_val = 0x089ABC0; +CONST char test_middle[] = ", test_val2 = 0x"; +BSS unsigned test_val2; +VARIABLE char test_after[] = ".\r\n"; + +#define PUSHALL "pushw %%ds; pushw %%es; pushw %%fs; pushw %%gs; pushal\n" +#define POPALL "popal; popw %%gs; popw %%fs; popw %%es; popw %%ds\n" + +CODE static void vgaputs (const char *string) +{ + while (*string != '\0') + asm(PUSHALL + " movw $0x0007,%%bx\n int $0x10 \n" + POPALL + : : "a" (0x0E00 | (unsigned char)*string++)); +} + +CODE static void vgaputx (unsigned val) +{ + unsigned digit_shift = 7 * 4; + + while (digit_shift && (val & (0xF << digit_shift)) == 0) + digit_shift -= 4; + for (;;) { + asm(PUSHALL + " movw $0x0007,%%bx \n" + " sahf # clear AF \n" + " aaa \n" + " aad $0x11 \n" + " add $0x0E30,%%ax \n" + " int $0x10 \n" + POPALL + : : "a" ((val >> digit_shift) & 0x0F)); + if (digit_shift == 0) + break; + digit_shift -= 4; + } +} + +CODE static inline void vga_wait_seconds (unsigned seconds) +{ + unsigned nb_microsec = seconds * 1000 * 1000; + unsigned short status; + + asm volatile (" int $0x15 # _BIOS_wait " + : "=a" (status) + : "a" ((unsigned short)0x8600), "d" (nb_microsec & 0xFFFF), + "c" (nb_microsec >> 16)); +} + +extern inline void test_vgaprint (void) +{ + unsigned stackptr; + static CONST char stkmsg[] = ", sp = 0x"; + + asm (" mov %%esp,%0 " : "=r" (stackptr)); + test_val2 = 0x1234fedc; + + vgaputs(test_before); + vgaputx(test_val); + vgaputs(test_middle); + vgaputx(test_val2); + vgaputs(stkmsg); + vgaputx(stackptr); + vgaputs(test_after); + vga_wait_seconds(7); +} + +#define DBG(startstr, variable, endstr) { \ + static CONST char before[]=startstr, after[] = endstr; \ + vgaputs(before); vgaputx(variable); vgaputs(after); \ + } +#define DBG_WAIT() vga_wait_seconds(7) +#else +#define test_vgaprint() /*nothing*/ +#define DBG(startstr, variable, endstr) /*nothing*/ +#define DBG_WAIT() /*nothing*/ +#endif + +/** + ** All the non BIOS related, low level functions we use: + **/ +extern inline unsigned peekb (farptr adr) +{ + unsigned returned; + unsigned short scratch; + extern char memory[]; + + asm(" pushl %2 \n" + " popw %w0 \n" + " popw %%fs \n" + " movzbl %%fs:(%w0),%1 \n" + : "=bSDB" (scratch), "=r" (returned) + : "g" (adr), "m" (*memory) ); + return returned; +} + +extern inline unsigned peekw (farptr adr) +{ + unsigned returned; + unsigned short scratch; + extern char memory[]; + + asm(" pushl %2 \n" + " popw %w0 \n" + " popw %%fs \n" + " movzwl %%fs:(%w0),%1 \n" + : "=bSDB" (scratch), "=r" (returned) + : "g" (adr), "m" (*memory) ); + return returned; +} + +extern inline void outb (unsigned short port, unsigned char data) +{ + asm volatile (" outb %0,%1 " : : "a" (data), "dN" (port)); +} + +extern inline unsigned char inb (unsigned short port) +{ + unsigned char result; + asm volatile (" inb %1,%0 " : "=a" (result) : "dN" (port)); + return result; +} + +extern inline void short_delay (unsigned count) +{ + asm volatile (" loopl . \n" : "+c" (count)); +} + +extern inline unsigned char getnvram (unsigned char addr) +{ + outb(0x70, addr & ~0x80); /* bit 7 cleared: NMI enabled */ + short_delay(1000); /* wait 1000 assembly instructions */ + return inb(0x71); +} + +__attribute__ ((const)) extern inline farptr +stack_farptr (const void *data) +{ + unsigned stackseg_mask; /* stack is max 64 K */ + + asm (" mov %%ss,%0 \n" : "=r" (stackseg_mask)); + return (farptr)((stackseg_mask << 16) | (unsigned) data); +} + +/** + ** Generic BIOS calls: + **/ + +extern inline struct shift_flags_str { + unsigned char right_shift_key_pressed : 1; + unsigned char left_shift_key_pressed : 1; + unsigned char control_key_pressed : 1; + unsigned char alternate_key_pressed : 1; + unsigned char scroll_lock_active : 1; + unsigned char num_lock_active : 1; + unsigned char caps_lock_active : 1; + unsigned char insert_active : 1; +} _BIOS_get_shift_flags (void) +{ + struct shift_flags_str flags; + + asm (" int $0x16 # _BIOS_get_shift_flags" + : "=a" (flags) + : "a" ((unsigned short)0x0200) ); + return flags; +} + +extern inline farptr _BIOS_getConfiguration (void) +{ + unsigned char error; + farptr conf; + + asm (" pushl %%es \n" + " stc \n" + " int $0x15 # _BIOS_getConfiguration \n" + " setc %%al \n" + " pushw %%es \n" + " pushw %%bx \n" + " popl %%ebx \n" + " popl %%es \n" + : "=a" (error), "=b" (conf) + : "a" ((unsigned short)0xC000) ); + if (error) + return (farptr)0; + return conf; +} + +extern inline void +get_bios_conf (farptr adr, struct sys_desc_table_info *sys_desc_table) +{ + unsigned nbbyte = sizeof(struct sys_desc_table_info); + + asm (" pushl %%esi \n" + " popw %%si \n" + " popw %%fs \n" + " cld \n" + " rep movsb %%fs:(%%si),%%es:(%%di) \n" + : "+S" (adr), "+D" (sys_desc_table), "+c" (nbbyte), "=X" (*sys_desc_table) + ); +} + +extern inline unsigned char +_BIOS_getExtendedMemory (unsigned short *sizeinKb) +{ + unsigned char error; + + asm (" int $0x15 # _BIOS_getExtendedMemory \n" + " setc %0 \n" + : "=qm" (error), "=a" (*sizeinKb) + : "a" ((unsigned short)0x8800) + ); + return error; /* 0 if no error */ +} + +struct MemoryMap_str { + unsigned long long base, length; + enum { MemAvailable = 1, MemSpecial = 2, + Mem_ACPI_reclaim = 3, Mem_ACPI_NVS = 4 + } type:32; +} __attribute__ ((packed)); + +extern inline unsigned +_BIOS_QueryMemoryMap (unsigned *cont_val, unsigned bufsize, + struct e820map_info *buffer) +{ + /* inline */ const unsigned smapsig = 0x534D4150; /* 'SMAP' in big endian */ + unsigned smap_res = 0xE820U, actual_len; + + asm volatile (" int $0x15 # _BIOS_QueryMemoryMap \n" + " jnc 1f \n" + " xorl %%eax,%%eax \n" + " 1: \n" + : "+a" (smap_res), "+b" (*cont_val), + "=c" (actual_len), "=X" (*buffer) + : "c" (bufsize), "d" (smapsig), "D" (buffer) /* in fact %%es:%%di */ + ); + if (smap_res != smapsig) + return 0; + return actual_len; +} + +extern inline unsigned char +_APM_installation_check (unsigned short *BCD_version, unsigned short *flags) +{ + unsigned char error; + unsigned short sig; + + asm (" int $0x15 # _APM_installation_check \n" + " setc %0 \n" + : "=qm" (error), "=a" (*BCD_version), "=b" (sig), "=c" (*flags) + : "a" ((unsigned short)0x5300), "b" (0) + ); + if (error) { + *BCD_version &= 0xFF00; /* keep there error code */ + *flags = 0; + } + return error || sig != 0x504D; /* "PM" */ +} + +extern inline unsigned short _APM_disconnect (void) +{ + unsigned short error; + + asm volatile (" int $0x15 # _APM_disconnect \n" + " setc %%al \n" + : "=a" (error) + : "a" ((unsigned short)0x5304), "b" (0) ); + if ((error & 0xFF) == 0) + return 0; + return error; +} + +extern inline unsigned char +_APM_connect_32bit (unsigned short *RM_segbase_32, + unsigned *entry_point_offset_32, + unsigned short *RM_segbase_code16, + unsigned short *RM_segbase_data16, + /* APM v1.1: */ + unsigned short *APM_BIOS_codeseg_len, + unsigned short *APM_BIOS_dataseg_len) +{ + unsigned char error; + + asm volatile (" xorl %%edi,%%edi \n" + " xorl %%esi,%%esi \n" + " int $0x15 # _APM_connect_32bit \n" + " setc %0 \n" + : "=qm" (error), + "=a" (*RM_segbase_32), "=b" (*entry_point_offset_32), + "=c" (*RM_segbase_code16), "=d" (*RM_segbase_data16), + "=S" (*APM_BIOS_codeseg_len), + "=D" (*APM_BIOS_dataseg_len) + : "a" ((unsigned short)0x5303), "b" (0) + ); + return error; +} + +/** + ** Video BIOS: EGA, VGA, VESA and EDID. + **/ +union EGA_bx_union { + struct { + enum { EGA_64K, EGA_128K, EGA_192K, EGA_256K } memory:8; + enum { EGA_COLOR_3Dx, EGA_MONO_3Bx } IOaddr:8; + } enums; + unsigned short IOaddr_memory; +} __attribute__ ((packed)); + +struct EGA_cx_str { + unsigned short switch1off : 1; + unsigned short switch2off : 1; + unsigned short switch3off : 1; + unsigned short switch4off : 1; + unsigned short switch_unused : 4; + unsigned short FEAT1line_state2 : 1; + unsigned short FEAT0line_state2 : 1; + unsigned short FEAT1line_state1 : 1; + unsigned short FEAT0line_state1 : 1; + unsigned short FEAT_unused : 4; +} __attribute__ ((packed)); + +extern inline void +_EGA_getdisplay (union EGA_bx_union *EGA_bx, struct EGA_cx_str *EGA_cx) +{ + unsigned short destroyed_tseng_ET4000_V8_00; + asm (" int $0x10 # _EGA_getdisplay " + : "=b" (*EGA_bx), "=c" (*EGA_cx), + "=a" (destroyed_tseng_ET4000_V8_00) + : "a" ((unsigned short)0x1200), + "b" ((unsigned short)0xFF10) /* %bh = 0xFF to see if supported */ + ); +} + +extern inline void +_VGA_getcursor (unsigned char page, unsigned char *row, unsigned char *col, + unsigned char *cursorstart, unsigned char *cursorstop) +{ + unsigned short row_col, cstart_cstop; + unsigned short destroyed_Phoenix_BIOS; + + asm (" int $0x10 # getcursor " + : "=d" (row_col), "=c" (cstart_cstop), + "=a" (destroyed_Phoenix_BIOS) + : "a" ((unsigned short)0x0300), "b" ((unsigned short)page << 8) + ); + *col = (unsigned char)row_col; + *row = row_col >> 8; + *cursorstart = cstart_cstop >> 8; /* bug reported with mono displays */ + *cursorstop = (unsigned char)cstart_cstop; +} + +extern inline char +_VGA_getmode (unsigned char *nbcol, unsigned char *page) +{ + unsigned short nbcol_mode; + unsigned short tmppage; + + asm (" int $0x10 # getmode " + : "=b" (tmppage), "=a" (nbcol_mode) + : "a" ((unsigned short)0x0F00) + ); + *nbcol = nbcol_mode >> 8; + *page = tmppage >> 8; + + return (char)nbcol_mode; /* bit 7 set if last setmode had it */ +} + +extern inline unsigned +_VGA_get_display_combination_code (unsigned char *active_dcc, + unsigned char *alternate_dcc) +{ + unsigned char result; + unsigned short alternate_active; + + asm (" int $0x10 # get_display_combination_code " + : "=a" (result), "=b" (alternate_active) + : "a" ((unsigned short)0x1A00) + ); + *active_dcc = (unsigned char)alternate_active; + *alternate_dcc = alternate_active >> 8; + + return (result != 0x1A); +} + +typedef struct { + char VbeSignature[4]; /* "VESA" but NOT zero terminated. */ + unsigned short VbeVersion; + farptr OemStringPtr; + struct vesa_capabilities_str Capabilities; + farptr VideoModePtr; + unsigned short TotalMemory; /* in 64K blocks */ + /* if VBE 2.0+ (else structure is 256 bytes) : */ + unsigned short OemSoftwareRev; + farptr OemVendorNamePtr; + farptr OemProductNamePtr; + farptr OemProductRevPtr; + + unsigned char Reserved[222]; /* ONLY VESA version 1.2- info */ + unsigned char OemData[256]; /* will not kill VESA1.2- */ +} __attribute__ ((packed)) VESA_VbeInfoBlock; + +typedef struct { + struct vesa_mode_attribute_str ModeAttributes; + struct { + unsigned char win_exist : 1; + unsigned char win_readable : 1; + unsigned char win_writable : 1; + unsigned char padding : 5; + } __attribute__ ((packed)) WinAAttributes, WinBAttributes; + unsigned short WinGranularity; /* in Kb */ + unsigned short WinSize; /* in Kb */ + unsigned short WinASegment; + unsigned short WinBSegment; + farptr WinFuncPtr; + unsigned short BytesPerScanLine; + /* optional part, see "attribute.optional_info_available": */ + unsigned short XResolution; /* char in text modes, + pixel in graphic modes */ + unsigned short YResolution; + unsigned char XCharSize; + unsigned char YCharSize; + unsigned char NumberOfPlanes; + unsigned char BitsPerPixel; + unsigned char NumberOfBanks; /* unused here : no multipaged screen */ + enum { mem_text = 0, mem_CGA, mem_HGC, mem_EGA, mem_packedpixel, + mem_seq256, mem_HiColor24, mem_YUV + } MemoryModel : 8; + /* mem_seq256 = non chain 4 */ + unsigned char BankSize; /* in Kb, unused here */ + unsigned char NumberOfImagePages; + unsigned char Reserved; + /* VBE 1.2+: */ + struct vesa_color_layout_str layout; + struct { + unsigned char color_ramp_programmable : 1; + unsigned char reserved_space_usable : 1; + } __attribute__ ((packed)) DirectColorModeInfo; + /* VBE 2.0: */ + unsigned PhysBasePtr; + unsigned OffScreenMemOffset; + unsigned short OffScreenMemSize; /* in Kb */ + unsigned char Reserved2[206]; +} __attribute__ ((packed)) VESA_modeinfo_t; + +extern inline unsigned +_VESA_getinfo (VESA_VbeInfoBlock *VESA_info) +{ + unsigned short cmd_status = 0x4F00; + + if (sizeof(VESA_VbeInfoBlock) != 512) + __ERROR(); + + asm (" int $0x10 # _VESA_getinfo " + : "+a"(cmd_status), "=X" (*VESA_info) + : "D"(VESA_info) /* in fact %es:%di */ + ); + return (cmd_status != 0x004F); +} + +extern inline unsigned +_VESA_getmodeinfo (VESA_modeinfo_t *VESA_modeinfo, unsigned short mode) +{ + unsigned short cmd_status = 0x4F01; + + if (sizeof(VESA_modeinfo_t) != 256) + __ERROR(); + + asm (" int $0x10 # _VESA_getmodeinfo " + : "+a" (cmd_status), "=X" (*VESA_modeinfo) + : "c" (mode), "D" (VESA_modeinfo) /* in fact %es:%di */ + ); + return (cmd_status != 0x004F); +} + +extern inline unsigned _VESA_getmode (unsigned short *mode) +{ + unsigned short cmd_status = 0x4F03; + + asm (" int $0x10 # _VESA_getmode " + : "+a" (cmd_status), + "=b" (*mode) /* *mode contains bits 13, 14 & 15 */ + ); + return (cmd_status != 0x004F); +} + +extern inline unsigned _VESA_setDACwidth (unsigned char depth) +{ + unsigned short cmd_status = 0x4F08; + unsigned short result = depth << 8; /* %bl = 0 */ + + asm volatile (" int $0x10 # _VESA_setDACwidth " /* VESA 1.2+ */ + : "+a" (cmd_status), "+b" (result) + ); + return (cmd_status != 0x004F || (result >> 8) != depth); +} + +/* VESA 2.0+ */ +extern inline unsigned +_VESA_GetPMinterface (farptr *addr, unsigned short *len) +{ + unsigned short cmd_status = 0x4F0A; + + asm (" pushl %%es \n" + " xorw %%di,%%di \n" + " mov %%di,%%es \n" + " int $0x10 # _VESA_GetPMinterface \n" + " pushw %%es \n" + " pushw %%di \n" + " popl %%edi \n" + " popl %%es \n" + : "+a" (cmd_status), "=D" (*addr), "=c" (*len) + : "b" (0), "c" ((unsigned short)0) + ); + + return (cmd_status != 0x004F); +} + +extern inline unsigned _EDID_detect (void) +{ + unsigned short cmd_status = 0x4F15, unknown1, unknown2; + /* %cx modified by NVIDIA RIVA TNT2 Model 64/Model 64 Pro */ + + asm ( +" pushw %%es \n" +" movw %%di,%%es \n" +" int $0x10 # _EDID_detect \n" +" popw %%es \n" + : "+a" (cmd_status), "=b" (unknown1), "=c" (unknown2) + : "b" (0), "c" (0), "D" (0) + ); + return (cmd_status != 0x004F); +} + +extern inline unsigned _EDID_read (VBE_EDID_t *info) +{ + unsigned short cmd_status = 0x4F15; + unsigned char tmp = 1; + unsigned short unknown1 = 0, unknown2 = 1; + + if (sizeof (VBE_EDID_t) != 0x80) + __ERROR(); + + asm (" int $0x10 # _EDID_read" + : "+a" (cmd_status), "=X" (*info) + : "b" (tmp), "D" (info), /* in fact %es:%di */ + "c" (unknown1), "d" (unknown2) + ); + /* returns 0x014F if monitor non EDID */ + return (cmd_status != 0x004F); +} + +/** + ** Disk BIOS: + **/ +extern inline unsigned +_BIOSDISK_getparam (unsigned char disk, + unsigned short *tmpcx, unsigned short *tmpdx, + farptr *DBTadr, unsigned char *CmosDriveType, + unsigned char *status) +{ + unsigned char carry; + + asm (" pushl %%es \n" + " xorw %%di,%%di \n" + " mov %%di,%%es \n" + " int $0x13 # _BIOSDISK_getparam \n" + " pushw %%es \n" + " pushw %%di \n" + " popl %%edi \n" + " popl %%es \n" + " mov %%ah,%1 \n" + " setc %0 \n" + : "=qm" (carry), "=qm" (*status), "=c" (*tmpcx), "=d" (*tmpdx), + "=b" (*CmosDriveType), "=D" (*DBTadr) + : "a"((unsigned short)0x0800), "d" (disk), "b" (0) + ); + + return carry; +} + +extern inline unsigned char +_BIOSDISK_gettype (unsigned char disk, unsigned *nbsect, unsigned char *status) +{ + unsigned char carry; + unsigned dummy; + + asm (" xor %%dh,%%dh \n" + " xor %%cx,%%cx \n" + " int $0x13 # _BIOSDISK_gettype \n" + " shll $16,%%ecx \n" + " movw %%dx,%%cx \n" + " movb %%ah,%1 \n" + " setc %0 \n" + : "=qm" (carry), "=qm" (*status), "=c" (*nbsect), "=d" (dummy) + : "a" ((unsigned short)0x1500), + "d" (disk) /* disk = 0x80..0x8F else old Compaq bug */ + ); + + return carry; +} + +extern inline void +get_drive_info (farptr indirect_ptr, union drive_info *diskinfo) +{ + unsigned nbbyte = sizeof(*diskinfo); + + asm (" pushl %%esi \n" + " popw %%si \n" + " popw %%fs \n" + " pushl %%fs:(%%si) \n" + " popw %%si \n" + " popw %%fs \n" + " cld \n" + " rep movsb %%fs:(%%si),%%es:(%%di) \n" + : "+S" (indirect_ptr), "+D" (diskinfo), "+c" (nbbyte), "=X" (*diskinfo) + ); +} + +extern inline unsigned char +_BIOSDISK_RWsector (unsigned char drive, + unsigned short cylinder, unsigned char head, + unsigned char sector, unsigned char nbsector, + farptr buffer, const unsigned read) +{ + unsigned short result, buggy_bios; + + asm volatile (" pushl %%es \n" + " pushl %%ebx \n" + " popw %%bx \n" + " popw %%es \n" + " stc # buggy BIOSes \n" + " int $0x13 # _BIOSDISK_RWsector \n" + " sti # buggy BIOSes \n" + " popl %%es \n" + " setc %%al \n" + : "=a" (result), "=d" (buggy_bios) + : "a" ((read? 0x0200 : 0x0300) | nbsector), + "b" (buffer), + "c" ((cylinder << 8) | ((cylinder >> 2) & 0xC0) + | (sector & 0x3F)), + "d" ((((unsigned short)head) << 8) | drive) + ); + if (result & 0x00FF) + return result >> 8; + return 0; +} + +typedef struct { + unsigned short buffersize; + unsigned short infobit; + unsigned nbcylinder; + unsigned nbhead; + unsigned nbsectorpertrack; + unsigned long long nbtotalsector; + unsigned short bytepersector; + /* V2.0+ : */ + farptr configptr; /* for instance ebiosPhoenix realmode address */ + /* V3.0+ : */ + unsigned short signature0xBEDD; + unsigned char lendevicepath; /* 0x24 for v3.0 */ + unsigned char reserved[3]; + unsigned char bustype[4]; /* exactly "ISA" or "PCI" */ + unsigned char Interface[8]; /* exactly "ATA", "ATAPI", + "SCSI", "USB"... */ + union interface_path_u { + struct { + unsigned short addr; + unsigned char pad[6]; + } __attribute__ ((packed)) isa; + struct { + unsigned char busnr; + unsigned char devicenr; + unsigned char functionnr; + unsigned char pad[5]; + } __attribute__ ((packed)) pci; + } bus; + union device_path_u { + struct { + unsigned char slave; /* 0 if master, 1 if slave */ + unsigned char pad[7]; + unsigned long long reserved; /* Not present in some docs... */ + } __attribute__ ((packed)) ata; + struct { + unsigned char slave; /* 0 if master, 1 if slave */ + unsigned char logical_unit_nr; + unsigned char pad[6]; + } __attribute__ ((packed)) atapi; + struct { + unsigned char logical_unit_nr; + unsigned char pad[7]; + } __attribute__ ((packed)) scsi; + struct { + unsigned char tobedefined; + unsigned char pad[7]; + } __attribute__ ((packed)) usb; + struct { + unsigned long long FireWireGeneralUniqueID; + } __attribute__ ((packed)) ieee1394; + struct { + unsigned long long WorldWideNumber; + } __attribute__ ((packed)) FibreChannel; + } device; + unsigned char zero; + unsigned char bytechecksum; /* byte sum [0x1E..0x49] = 0 */ +} __attribute__ ((packed)) ebiosinfo_t; + +typedef struct { + unsigned short extended_fct : 1; + unsigned short removable : 1; + unsigned short enhanced : 1; + unsigned short reserved : 13; +} __attribute__ ((packed)) ebios_bitmap_t; + +extern inline unsigned char +_EBIOSDISK_probe (unsigned char disk, unsigned char *version, + unsigned char *extension, ebios_bitmap_t *bitmap) +{ + unsigned char carry; + unsigned short signat0xAA55; + + asm (" int $0x13 # _EBIOSDISK_probe \n" + " mov %%ah,%1 \n" + " mov %%dh,%2 \n" + " setc %0 \n" + : "=qm" (carry), "=qm" (*version), "=qm" (*extension), + "=b" (signat0xAA55), "=c" (*bitmap) + : "a" ((unsigned short)0x4100), "b" ((unsigned short)0x55AA), + "d"(disk) + ); + + return carry || (signat0xAA55 != 0xAA55); +} + +extern inline unsigned char +_EBIOSDISK_getparam (unsigned char disk, ebiosinfo_t *ebiosinfo, + unsigned char *status) +{ + unsigned char carry; + + asm (" int $0x13 # _EBIOSDISK_getparam \n" + " mov %%ah,%1 \n" + " setc %0 \n" + : "=qm" (carry), "=qm" (*status) + : "a" ((unsigned short)0x4800), "d" (disk), "S" (ebiosinfo) + ); + + return carry; +} + +/* + * Well, I do not know what that does, but because it is included in Linux + * kernel it has to be GPL - source code given in its most readable form... + */ +extern inline void +_X86SpeedStepSmi (struct X86SpeedStepSmi_s *data) +{ + asm(" int $0x15 # _X86SpeedStepSmi " + : "=a" (data->data1), "=b" (data->data2), + "=c" (data->data3), "=d" (data->data4) + : "a" (0x0000E980), "d" (0x47534943) ); +} + +/** + ** Fill in each of the sub-structures: + **/ +CODE static inline void +vmlinuz_EGA (struct screen_info *screen_info) +{ + union EGA_bx_union EGA_bx; + struct EGA_cx_str EGA_cx; + + _EGA_getdisplay(&EGA_bx, &EGA_cx); + screen_info->orig_video_ega_bx = EGA_bx.IOaddr_memory; + screen_info->orig_video_isVGA = 0; + if (EGA_bx.enums.IOaddr == EGA_COLOR_3Dx + || EGA_bx.enums.IOaddr == EGA_MONO_3Bx) { + unsigned char active_dcc, alternate_dcc; + + if (_VGA_get_display_combination_code(&active_dcc, + &alternate_dcc) == 0) + screen_info->orig_video_isVGA = 1; + + screen_info->orig_video_lines = peekb((farptr)0x00400084) + 1; + screen_info->orig_font_height = peekw((farptr)0x00400085); + } else { /* EGA_bx.enums.IOaddr unchanged => CGA/MDA/HGA */ + screen_info->orig_video_lines = 25; + screen_info->orig_font_height = 0; /* i.e. invalid */ + } +} + +CODE static inline void +vmlinuz_getcursor (struct screen_info *screen_info) +{ +#if 1 /* what is the best ? IHMO using the BIOS functions */ + unsigned char row, col; + unsigned char cursorstart, cursorstop; + + _VGA_getcursor(screen_info->orig_video_page, + &row, &col, &cursorstart, &cursorstop); + screen_info->orig_x = col; + screen_info->orig_y = row; +#else + farptr ptr = 0x00400050 + 2 * screen_info->orig_video_page; + + screen_info->orig_x = peekb(ptr); + screen_info->orig_y = peekb(ptr + 1); +#endif +} + +CODE static inline void +vmlinuz_VGA (struct screen_info *screen_info, int proposed_line) +{ + unsigned char mode, nbcol, page; + + mode = _VGA_getmode(&nbcol, &page); + screen_info->orig_video_mode = mode & 0x7F; + screen_info->orig_video_cols = nbcol; + screen_info->orig_video_page = page; /* not tested if != 0 */ + if (proposed_line >= 0) { + screen_info->orig_y = proposed_line; + screen_info->orig_x = 0; + } else { + vmlinuz_getcursor(screen_info); + /* We try not to erase the text written later by Gujin: */ + screen_info->orig_x = 0; + if (screen_info->orig_y + 4 < screen_info->orig_video_lines) + screen_info->orig_y += 4; + else + screen_info->orig_y = screen_info->orig_video_lines - 1; + } +} + +CODE static inline void +vmlinuz_VESA (struct screen_info *screen_info, unsigned mode) +{ + VESA_modeinfo_t modeinfo; + + if (_VESA_getmodeinfo(&modeinfo, mode & 0x01FF) == 0) { + if (modeinfo.MemoryModel == mem_text + && modeinfo.NumberOfPlanes == 4) { + /* correct some buggy video BIOS: */ + modeinfo.BytesPerScanLine = 2 * modeinfo.XResolution; + modeinfo.BitsPerPixel = 4; + } + if (modeinfo.MemoryModel == mem_text) + screen_info->lfb_base = 0xB8000; + else if (modeinfo.ModeAttributes.linear_framebuffer_supported) { + /* no text modes with linear framebuffer */ + screen_info->orig_video_isVGA = 0x23; + screen_info->lfb_base = modeinfo.PhysBasePtr; + } else + screen_info->lfb_base = 0xA0000; + screen_info->lfb_linelength = modeinfo.BytesPerScanLine; + screen_info->lfb_width = modeinfo.XResolution; + screen_info->lfb_height = modeinfo.YResolution; + screen_info->lfb_depth = modeinfo.BitsPerPixel; + screen_info->lfb_pages = modeinfo.NumberOfImagePages; + screen_info->vesa_attrib = modeinfo.ModeAttributes; + screen_info->layout = modeinfo.layout; + } else + screen_info->lfb_base = 0; +} + +CODE static void +vmlinuz_VIDEO (struct screen_info *screen_info, unsigned short *acpi_vidmode, + int proposed_line) +{ + VESA_VbeInfoBlock infoblock; + + vmlinuz_EGA(screen_info); + vmlinuz_VGA(screen_info, proposed_line); + *acpi_vidmode = screen_info->orig_video_mode; + + /* normaly only for graphic modes: */ + if (_VESA_getinfo(&infoblock) == 0) { + unsigned short mode, len; + farptr addr; + + screen_info->lfb_size = infoblock.TotalMemory; + screen_info->capabilities = infoblock.Capabilities; + if (_VESA_getmode(&mode) == 0) { + vmlinuz_VESA(screen_info, mode); + *acpi_vidmode = mode; +#if 0 /* After doing that, we should program again the DAC with the right values... */ + /* Linux do not manage text modes and DAC 8 bits: */ + if (screen_info->lfb_base != 0xB8000 + && screen_info->lfb_depth <= 8 + && infoblock.Capabilities.DAC8bit_present) { + if (_VESA_setDACwidth (8) == 0) { + screen_info->layout = (const struct vesa_color_layout_str){ + {8, 0}, {8, 0}, {8, 0}, {8, 0}}; + } + else { + screen_info->layout = (const struct vesa_color_layout_str){ + {6, 0}, {6, 0}, {6, 0}, {6, 0}}; + } + } +#endif + } + + /* Note: We can have a PM interface even if getmodeinfo + failed, for instance if we are in text mode 3 */ + if (_VESA_GetPMinterface(&addr, &len) == 0) { + screen_info->vesapm_seg = addr >> 16; + screen_info->vesapm_off = addr & 0xFFFF; + } + } +} + +CODE static inline unsigned short vmlinuz_EXT_MEM_K (void) +{ + unsigned short EXT_MEM_K; + + if (_BIOS_getExtendedMemory(&EXT_MEM_K) != 0 || EXT_MEM_K == 0) + EXT_MEM_K = (((unsigned short)getnvram(0x18)) << 8) + + getnvram(0x17); + return EXT_MEM_K; +} + +CODE static inline void +vmlinuz_APM (struct apm_bios_info *apm_bios_info) +{ + if (_APM_installation_check(&apm_bios_info->version, + &apm_bios_info->flags) == 0) { + _APM_disconnect(); /* ignore possible error */ + if ((apm_bios_info->flags & 2) == 0) { + /* no 32 bits support, ignore APM */ + } else if (_APM_connect_32bit(&apm_bios_info->cseg, + &apm_bios_info->offset, + &apm_bios_info->cseg_16, + &apm_bios_info->dseg, + &apm_bios_info->cseg_len, + &apm_bios_info->dseg_len) != 0) { + apm_bios_info->flags &= ~2; /* remove 32bits support */ + } else { + /* Redo the installation check as the 32 bit connect + modifies the flags returned on some BIOSs : */ + if (_APM_installation_check(&apm_bios_info->version, + &apm_bios_info->flags) != 0) { + apm_bios_info->flags &= ~2; + _APM_disconnect(); + } + } + } +} + +CODE static inline void +vmlinuz_CONFIG (struct sys_desc_table_info *sys_desc_table) +{ + farptr adr = _BIOS_getConfiguration(); + + if (adr != 0) + get_bios_conf(adr, sys_desc_table); +} + +CODE static inline void +vmlinuz_EDID (VBE_EDID_t *edid_data) +{ + if (_EDID_detect() != 0 || _EDID_read(edid_data) != 0) { + unsigned char *ptr = (unsigned char *)edid_data; + unsigned cpt = sizeof(VBE_EDID_t); + /* failed, reset complete area: */ + while (--cpt) + *ptr++ = 0x13; + } +} + +CODE static inline unsigned +vmlinuz_E820 (struct e820map_info *e820map, unsigned e820map_bufsize) +{ + unsigned bufsize = e820map_bufsize, cont_val = 0, actual_len; + /* Use that to avoid data in .rodata with GCC-2.95.3: */ + unsigned element_size = sizeof(struct e820map_info); + + while ( bufsize >= element_size + && (actual_len = _BIOS_QueryMemoryMap(&cont_val, + bufsize, e820map)) != 0 + && cont_val != 0) { + bufsize -= actual_len; + e820map = (void *)e820map + actual_len; + } + return (e820map_bufsize - bufsize) / element_size; +} + +CODE static inline unsigned +vmlinuz_MBRSIG (unsigned *MBRSIG, unsigned total_MBRSIG_entries) +{ + unsigned buffer[512 / sizeof(unsigned)], cpt; +#define EDD_MBR_SIG_OFFSET 0x1B8 /* offset of signature in the MBR */ + + for (cpt = 0; cpt < total_MBRSIG_entries; cpt++) { + if (_BIOSDISK_RWsector(0x80 + cpt, 0, 0, 1, 1, + stack_farptr(buffer), 1) != 0) + return cpt; + *MBRSIG++ = buffer[EDD_MBR_SIG_OFFSET / sizeof(unsigned)]; + } + return cpt; +} + +CODE static inline unsigned +vmlinuz_EDD (struct edd_info *edd, unsigned total_EDD_entries) +{ + unsigned cpt, eddcpt; + + for (cpt = eddcpt = 0; cpt < 16 && eddcpt < total_EDD_entries; cpt++) { + unsigned char extension, status; + unsigned short tmpcx, tmpdx; + farptr DBTadr; + unsigned char CmosDriveType, stat; + struct edd_info *eddptr = &edd[eddcpt]; + + if (_EBIOSDISK_probe(0x80 + cpt, &eddptr->version, &extension, + (ebios_bitmap_t *) &eddptr->interface_support)) + continue; + eddptr->device = 0x80 + cpt; + eddptr->params.length = sizeof(eddptr->params); + eddptr->params.info_flags = 0; /* buggy BIOS ? */ + /* Don't check for fail return, it doesn't matter: */ + _EBIOSDISK_getparam(0x80 + cpt, + (ebiosinfo_t *) &eddptr->params, &status); + if (_BIOSDISK_getparam(0x80 + cpt, &tmpcx, &tmpdx, + &DBTadr, &CmosDriveType, &stat) != 0) { + eddptr->max_cyl = 0; + eddptr->max_sect = eddptr->max_head = 0; + } else { + eddptr->max_head = tmpdx >> 8; + eddptr->max_sect = tmpcx & 0x3F; + eddptr->max_cyl = ((tmpcx & 0xC0) << 2) | (tmpcx >> 8); + } + eddcpt++; + } + return eddcpt; +} + + +/** + ** Command line stuff: + **/ +CODE static inline void +vmlinuz_CMDLINE (char *command_line, const unsigned char *proposed_cmdline, + farptr extra_cmdline_farptr, + const char *cmdline_name, const char *cmdline_extraparam) +{ + char *cmdptr = command_line; + + /* insert at beginning the name of the uncompressed GZIP file: */ + if (cmdline_name && *cmdline_name) { + do + *cmdptr++ = *cmdline_name++; + while (*cmdline_name); + *cmdptr++ = ' '; + } + + /* insert then: console=ttyS?,9600,n,7 or video=vesa, + keyboard=??, NumLock=on/off, mouse=/dev/psaux, + COLS=, LINES= proposed by Gujin */ + if (proposed_cmdline && *proposed_cmdline) { + do + *cmdptr++ = *proposed_cmdline++; + while (*proposed_cmdline); + *cmdptr++ = ' '; + } + + /* Add nearby end the Gujin gujin_param.extra_cmdline field: */ + /* We assume there gujin_param.extra_cmdline is correctly zero terminated */ + /* Note: we will have "root=" parameter from Gujin setup screen first, + so that if the one in the GZIP comment if erroneous, we can just + overwrite it with the Gujin setup (TOCHECK: when two "root=" are + given on the command line, only the first one is taken) */ + if (extra_cmdline_farptr != 0 && peekb(extra_cmdline_farptr) != 0) { + *cmdptr++ = ' '; /* two spaces as separator */ + for (;;) { + *cmdptr = peekb(extra_cmdline_farptr++); + if (*cmdptr < ' ') { /* stop at \0, \n, \r or \t */ + *cmdptr = '\0'; + break; + } + cmdptr++; + } + } + + /* add at end the params in the comment part of the GZIP file: */ + /* This may or may not stop at the first newline */ + if (cmdline_extraparam && *cmdline_extraparam) { + *cmdptr++ = ' '; + do + *cmdptr++ = *cmdline_extraparam++; + while (*cmdline_extraparam != 0 && *cmdline_extraparam != '\n'); + } + *cmdptr = '\0'; +} + +/** + ** OPTIONAL: If we want to use the data structures of Gujin to try to + ** find and match the root filesystem of the kernel loaded (that part + ** can be completely disabled): + **/ +#ifdef ROOT_EXTENSIVE_SEARCH + +struct desc_str { + unsigned inode; + unsigned filesize; + unsigned char disk; + unsigned char partition; /* 0xFF for MBR */ + unsigned char name_offset; /* to after the pattern */ + enum curdir_e { inRoot = 0, inSlashBoot, inSlashImage } curdir : 3; + enum boottype_e { is_MBR, is_PBR, is_kernel_with_header, + is_kernel_without_header, is_initrd, + is_bdi_file, is_el_torito, is_initramfs } boottype : 3; + unsigned char unused : 2; +#define NAME_LENGTH 52 + char filename[NAME_LENGTH]; +} __attribute__ ((packed)); + +struct gpl_compliant_str { + unsigned signature; + unsigned version; + unsigned feature; + unsigned size; + struct setjmp_buf { + unsigned esi, edi, ebp, ebx; + unsigned short gs, fs, es, ds; + unsigned short flags, ip, cs, sp, ss; + } __attribute__ ((packed)) jmpbuf; + /* unsigned short padding */ + unsigned filename_array, gdt, regs, fourKbuffer; + unsigned LOADER, STATE, UI, MOUSE, DI, UTIL, BOOTWAY; +}; + +typedef struct { + unsigned char header_id_0x01; + enum { platform_80x86 = 0, platform_PowerPC = 1, + platform_Mac = 2 } platform_id : 8; + unsigned short reserved; + unsigned char manufacturer[0x1B - 0x04 +1]; + unsigned short checksum; + unsigned char signature1_0x55; + unsigned char signature2_0xAA; +} __attribute__ ((packed)) ISO9660_Validation_entry; + +typedef struct { + unsigned char boot_indicator; /* 0x88: bootable, 0x00: non bootable */ + enum { boot_media_noemul = 0, boot_media_120 = 1, boot_media_144 = 2, + boot_media_288 = 3, boot_media_hd = 4 } boot_media_type : 8; + unsigned short load_segment; /* 0x7c0 if null */ + unsigned char system_type; + unsigned char unused; + unsigned short sector_count; + unsigned sector_lba; + unsigned char reserved[0x1F - 0x0C +1]; +} __attribute__ ((packed)) ISO9660_Default_entry; + +struct diskparam_str { + enum { bios_chs, ebios_lba, + hardide_chs, hardide_lba, hardide_lba48, hardide_atapi, + dos_part + } access : 8; + unsigned char disknb; /* BIOS number or DOS letter + or 0=master, non zero=slave */ + unsigned char biostype; /* 1:floppy w/o change detect, + 2: with, 3: HD */ + unsigned char drivetype; /* 1: 360K, 2: 1.2M, 3: 720K, + 4: 1.44M, 6: 2.88M, 0x10: ATAPI */ + unsigned char diskname[32]; + + struct ide_attribute_str { + unsigned smart : 1; + unsigned host_protected_area : 1; + unsigned security : 1; + unsigned lba48 : 1; + unsigned removable : 1; + unsigned SAORAB : 1; + unsigned config_overlay : 1; + unsigned reserved : 25; + } ide_attribute; + struct error_log_str { + unsigned read_media : 1; + unsigned write_media : 1; + unsigned access_over_disk_tryed : 1; + unsigned access_over_partition_tryed : 1; + unsigned no_ebios_fct : 1; + unsigned ebios_size_zero : 1; + unsigned chs_bios_part_mismatch : 1; + unsigned chs_ajusted_bootsect : 1; + unsigned diskheader_ignored : 1; + unsigned disk_locked : 1; + unsigned disk_was_pw_locked : 1; + unsigned SMART_disabled : 1; + unsigned SMART_failure : 1; + unsigned analyse_partition_failed : 1; + unsigned NB_PARTITION_exceded : 1; + unsigned partition_overlap : 1; + unsigned partition_over_limit : 1; + unsigned beer_checksum_error : 1; + unsigned beer_in_extended_partition : 1; + unsigned reserved : 13; + } error_log; + unsigned short bytepersector; + unsigned short nbhead; /* DOS: MediaDescriptorByte */ + unsigned nbcylinder; /* DOS: ClustersOnDisk */ + unsigned BEER_size; + unsigned long long BEER_sector; + unsigned long long BEER_HostProtectedArea_start; + unsigned long long BEER_ReservedAreaBootCodeAddress; + unsigned long long BEER_disksize; + signed long long fulldisk_offset; /* Nb sectors before MBR */ + unsigned long long fulldisk_size; /* only if MBR is offseted */ + unsigned long long nbtotalsector; + unsigned long long config_max_lba; + unsigned short nbsectorpertrack; /* DOS: SectorPerCluster */ + + unsigned short infobit; /* Int13/48 extensions */ + unsigned char ebios_version; /* if present, used if access = ebios */ + unsigned char ebios_extension; + ebios_bitmap_t ebios_bitmap; + unsigned short reserved3; /* ebios_bitmap is 16 bits */ + + unsigned char ebios_bustype[4]; /* exactly "ISA" or "PCI" */ + unsigned char ebios_Interface[8]; /* exactly "ATA", "ATAPI", + "SCSI", "USB"...*/ + union interface_path_u ebios_bus; + union device_path_u ebios_device; + + unsigned short reserved4; + + unsigned short ide_master_password_revision; /* if command_supported1.security */ + unsigned short ideIOadr; /* if known, else 0 */ + unsigned short ideIOctrladr; /* if known, else 0 */ + unsigned char lba_slave_mask; /* see Phenix Bios (head | mask) */ + unsigned char irq; /* if known, else 0 */ + unsigned char multiplemode; /* int 0x13/0x21,0x22,0x24 or hardide, + set but not used. */ + unsigned char BEER_device_index; + struct config_multiword_dma_s { + unsigned short mode0 : 1; + unsigned short mode1 : 1; /* and below */ + unsigned short mode2 : 1; /* and below */ + unsigned short reserved : 13; + } __attribute__ ((packed)) initial_multiDMA, maximum_multiDMA; + struct config_ultra_dma_s { + unsigned short mode0 : 1; + unsigned short mode1 : 1; /* and below */ + unsigned short mode2 : 1; /* and below */ + unsigned short mode3 : 1; /* and below */ + unsigned short mode4 : 1; /* and below */ + unsigned short mode5 : 1; /* and below */ + unsigned short reserved : 10; + } __attribute__ ((packed)) initial_ultraDMA, maximum_ultraDMA; + struct config_feature_s { + unsigned short smart_feature : 1; + unsigned short smart_selftest : 1; + unsigned short smart_errorlog : 1; + unsigned short security : 1; + unsigned short standby_powerup : 1; + unsigned short RW_DMA_queued : 1; + unsigned short acoustic : 1; + unsigned short host_prot_area : 1; + unsigned short lba48 : 1; + unsigned short reserved : 7; + } __attribute__ ((packed)) initial_feature, maximum_feature; + unsigned CalibrationIdeloop; /* nanoseconds (10^-9) per loop */ + + /* Following three only for C/H/S partition, from standard BIOS: */ + unsigned short bios_nbcylinder; /* max 1024 */ + unsigned char bios_nbhead; /* max 256 */ + unsigned char bios_nbsectorpertrack; /* max 63 */ + + unsigned char bootable; /* nonzero: bootable, + > 1 when special bootsector recognised */ + unsigned char OSdisknumber; /* as written in the boot block */ + + unsigned short nbpartition; + unsigned short nbOSnumber; + unsigned short reserved5; + /* Following value is current extended primary partition, + if there is more than one extended primary partition. */ + unsigned first_ext_partition_start; /* NOT A LONG LONG */ + struct partition_str { + unsigned long long start, length; + /* OSnumber should be equal to the # in /dev/hda#: */ + unsigned char type, OSnumber; + struct partition_misc_str { + /* order as found on disk: */ + unsigned char order : 6; + unsigned char fsanalyse_toobig : 1; + unsigned char fsanalyse_error : 1; + unsigned char maybe_root : 1; + unsigned char fat_bootable : 1; + unsigned char beer_partition : 1; + unsigned char swap_partition : 1; + unsigned char reserved : 2; + enum part_state { + part_invalid = 0, part_active, + part_inactive, part_extended + } active : 2; + } __attribute__ ((packed)) misc; +#define MAX_DISKNAMESIZE (64 - 20 - 4) /* bigger than "floppy" */ + unsigned char name[MAX_DISKNAMESIZE]; + union { + ISO9660_Validation_entry *validation; /* first one only */ + ISO9660_Default_entry *catalog; /* array index >= 1 */ + } ElTorito; + } __attribute__ ((packed)) *partition; + + unsigned nbfreelist; + struct freelist_str { + unsigned long long start, length; + } *freelist; + char set_max_password[32]; + } *param; + +struct disk_interface_str { + unsigned nbdisk; + unsigned char max_disk, max_partition, max_freelist, max_IDE_found; + unsigned short sizeof_diskparam_str, sizeof_partition_str; + unsigned short sizeof_freelist_str, sizeof_IDE_found_str; + unsigned char nb_bios_fd, nb_bios_hd, cannot_reset_floppy, + nb_bios_blacklist; + unsigned char bios_blacklist[4]; + unsigned reserved1[2]; + unsigned char (*readsector) (struct diskparam_str *dp, int partition, + unsigned long long lba, unsigned number, + farptr buffer); + unsigned char (*writesector) (struct diskparam_str *dp, int partition, + unsigned long long lba, unsigned number, + farptr buffer); + int (*ideSecurity) (struct diskparam_str *, char *, + unsigned); + unsigned long long last_lba_read_or_write; + struct diskparam_str *param; + unsigned nb_IDE_found; + struct IDE_found_str { + unsigned short ideIOadr; + unsigned short ideIOctrladr; + unsigned char irq; + unsigned char bios_order; + unsigned short reserved; + } *IDE_found; +}; + +CODE static inline int +add_hexletter (unsigned short *val, unsigned char hexletter) +{ + if (hexletter >= '0' && hexletter <= '9') + *val = (*val << 4) + hexletter - '0'; + else if (hexletter >= 'a' && hexletter <= 'f') + *val = (*val << 4) + hexletter - 'a' + 10; + else if (hexletter >= 'A' && hexletter <= 'F') + *val = (*val << 4) + hexletter - 'A' + 10; + else + return 1; + return 0; +} + +/* If the "ide[0-9]=" are already given on the command line, that may + indicate the user wants a special order - and we probe that order + to guess the root partition: */ +CODE static inline void +init_IDE_found_from_cmdline (struct IDE_found_str *IDE_found, + const unsigned nb_IDE_found, const char *cmdptr) +{ + while (*cmdptr) { + if (cmdptr[0] == 'i' && cmdptr[1] == 'd' && cmdptr[2] == 'e' + && cmdptr[3] >= '0' && cmdptr[3] <= '9' + && cmdptr[4] == '=' + && cmdptr[5] == '0' && cmdptr[6] == 'x') { + unsigned short IOaddr = 0, IOctrladr = 0; + unsigned short index = cmdptr[3] - '0'; + unsigned char irq = 0; + + cmdptr += 7; + while (add_hexletter (&IOaddr, *cmdptr) == 0) + cmdptr++; + /* Why did you read up to here? Don't you trust me? */ + if (cmdptr[0] == ',' && cmdptr[1] == '0' + && cmdptr[2] == 'x') { + cmdptr += 3; + while (add_hexletter (&IOctrladr, *cmdptr) == 0) + cmdptr++; + if (*cmdptr == ',') { + cmdptr++; + while (*cmdptr >= '0' && *cmdptr <= '9') + irq = (irq * 10) + *cmdptr++ - '0'; + } + } + + /* first unknown char shall be spacing or end of string */ + if ( (*cmdptr == ' ' || *cmdptr == '\0') + && index < nb_IDE_found) + IDE_found[index] = (struct IDE_found_str) { + .ideIOadr = IOaddr, + .ideIOctrladr = IOctrladr, + .irq = irq, + .bios_order = 0xFF, + .reserved = 0 + }; + } + else + cmdptr++; + } +} + +/* If no "ide[0-9]=" given, we use the BIOS order found by Gujin; + if Gujin knows more IDEs (just one ide added like: ide2=0x... + but not ide0= and ide1=) add the obvious missing ones, + without erasing nor offseting the user given information: */ +CODE static inline void +init_IDE_found_from_DI (struct IDE_found_str *IDE_found, + const unsigned nb_IDE_found, + const struct disk_interface_str *DI) +{ + unsigned cpt; + + for (cpt = 0; cpt < DI->nb_IDE_found; cpt++) { + unsigned i, j; + for (i = 0; i < nb_IDE_found; i++) + if (IDE_found[i].ideIOadr == DI->IDE_found[cpt].ideIOadr) + break; + if (i < nb_IDE_found) + continue; /* already given */ + for (j = 0; j < nb_IDE_found; j++) + if (IDE_found[j].ideIOadr == 0) + break; /* first unused */ + if (j < nb_IDE_found) + IDE_found[j] = DI->IDE_found[cpt]; + } +} + +CODE static inline void +finalise_cmdline (struct IDE_found_str *IDE_found, unsigned nb_IDE_found, + char *command_line) +{ + static CONST char itoa_array[] = "0123456789abcdef"; + unsigned cpt; + char *cmdptr = command_line; + + while (*cmdptr != '\0') + cmdptr++; + for (cpt = 0; cpt < nb_IDE_found; cpt++) { + unsigned short val; + if ( IDE_found[cpt].ideIOadr == 0 + || IDE_found[cpt].bios_order == 0xFF) + continue; + if (cmdptr - command_line > 1024) + break; + *cmdptr++ = ' '; + *cmdptr++ = 'i'; *cmdptr++ = 'd'; *cmdptr++ = 'e'; + *cmdptr++ = '0' + cpt; + *cmdptr++ = '='; *cmdptr++ = '0'; *cmdptr++ = 'x'; + val = IDE_found[cpt].ideIOadr; + if (val & 0xF000) + *cmdptr++ = itoa_array[val >> 12]; + *cmdptr++ = itoa_array[(val >> 8) & 0x0F]; + *cmdptr++ = itoa_array[(val >> 4) & 0x0F]; + *cmdptr++ = itoa_array[(val >> 0) & 0x0F]; + val = IDE_found[cpt].ideIOctrladr; + if (val) { + *cmdptr++ = ','; + *cmdptr++ = '0'; + *cmdptr++ = 'x'; + if (val & 0xF000) + *cmdptr++ = itoa_array[val >> 12]; + *cmdptr++ = itoa_array[(val >> 8) & 0x0F]; + *cmdptr++ = itoa_array[(val >> 4) & 0x0F]; + *cmdptr++ = itoa_array[(val >> 0) & 0x0F]; + val = IDE_found[cpt].irq; /* < 100 */ + if (val) { + *cmdptr++ = ','; + *cmdptr++ = itoa_array[val / 10]; + *cmdptr++ = itoa_array[val % 10]; + } + } + *cmdptr = '\0'; + } +} + +/* root= parameter not present, set it depending on system_desc->disk, + system_desc->partition and the number of slash in the kernel name as + found in the GZIP name field. + Note that we assume that IF the kernel file loaded was in the top + directory AND the partition size is too small, it is a "/boot" + partition and the real root is the NEXT partition of type 0x83 if it exists. + Gujin prefers a lot having only one root partition containning everything + including "/boot", but try to stay compatible. */ +CODE static inline void +add_root_parameter (const struct desc_str *system_desc, char *cmdptr, + const struct IDE_found_str IDE_found[10], + const unsigned nb_IDE_found, + const struct disk_interface_str *DI, + const unsigned nb_slash_in_kernelname) + { + const struct diskparam_str *const dp = &DI->param[system_desc->disk]; + const unsigned min_part_size = 640 * 1024 * 1024 / dp->bytepersector; + unsigned OSnumber = dp->partition[system_desc->partition].OSnumber; + static CONST char root_pattern[] = " root=/dev/"; + + if ( system_desc->curdir == inRoot + && nb_slash_in_kernelname != 1 /* not really created in root dir */ + && dp->partition[system_desc->partition].length < min_part_size) { + /* find the next bigger 0x83 partition on same disk: */ + unsigned partition; + for (partition = system_desc->partition; + partition < dp->nbpartition; + partition++) + if ( dp->partition[partition].length > min_part_size + && dp->partition[partition].type == 0x83) { + OSnumber = dp->partition[partition].OSnumber; + break; + } + } + + if (dp->ideIOadr != 0) { + /* seriously looks like an IDE disk, isn't it? */ + unsigned cptide; + for (cptide = 0; cptide < nb_IDE_found; cptide++) + if (dp->ideIOadr == IDE_found[cptide].ideIOadr) + break; + if (cptide < nb_IDE_found) { + const char *ptr = root_pattern; + while (*ptr) + *cmdptr++ = *ptr++; + *cmdptr++ = 'h'; + *cmdptr++ = 'd'; + *cmdptr++ = 'a' + 2 * cptide + !!(dp->lba_slave_mask & 0x10); + *cmdptr++ = '0' + OSnumber; + *cmdptr = '\0'; + } + } else { + /* count number of [E]BIOS hard disks and compare to the + number of IDE hard disk to know if there are SCSI disks: */ + unsigned cpt_bios_disk = 0, cpt_ide_disk = 0, cptdisk; + unsigned char maybe_scsi = 0; + const char *ptr = root_pattern; + + for (cptdisk = 0; cptdisk < DI->nbdisk; cptdisk++) { + if ( DI->param[cptdisk].access == bios_chs + || DI->param[cptdisk].access == ebios_lba) { + if (DI->param[cptdisk].disknb >= 0x80) { + cpt_bios_disk++; + if (DI->param[cptdisk].ideIOadr == 0) + maybe_scsi = 1; + } /* else floppy disk */ + } + else if (DI->param[cptdisk].access == hardide_chs + || DI->param[cptdisk].access == hardide_lba + || DI->param[cptdisk].access == hardide_lba48) + cpt_ide_disk++; /* not CDROM, not empty IDE */ + } + while (*ptr) + *cmdptr++ = *ptr++; + /* If we have the same amount or less BIOS disks than IDE + disks, we assume we have a normal (or a very old w/o EBIOS) PC + without SCSI or USB drives, then assume BIOS mapping like + IDE mapping: 0x80 master on IDE0; else we assume all SCSI + drives are after the BIOS/IDE drives: */ + if (maybe_scsi != 0 && cpt_bios_disk > cpt_ide_disk + && dp->disknb >= 0x80 + cpt_bios_disk) + *cmdptr++ = 's'; + else { + cpt_bios_disk = 0; /* no sda to hda correction */ + *cmdptr++ = 'h'; + } + *cmdptr++ = 'd'; + /* If bootsector OSdisknumber initialised to something + else than 0 or 0x80, we assume the user forced + it and use that value ('a' for number 0x80): */ + if ((dp->OSdisknumber & 0x7F) != 0) + *cmdptr++ = 'a' + (dp->OSdisknumber & 0x7F); + else + *cmdptr++ = 'a' + dp->disknb - 0x80 - cpt_bios_disk; + *cmdptr++ = '0' + OSnumber; + *cmdptr = '\0'; + } +} + +CODE static void +vmlinuz_ROOT (const struct desc_str *system_desc, + const struct gpl_compliant_str *togpl, + char *command_line, unsigned nb_slash_in_kernelname) +{ + const struct disk_interface_str *DI = + (const struct disk_interface_str *)togpl->DI; + char *cmdptr; +#if 0 /* problem with linker, so do not call memset */ + struct IDE_found_str IDE_found[10] = {}; +#else + struct IDE_found_str IDE_found[10]; + unsigned i = sizeof (IDE_found) / sizeof(IDE_found[0]); + while (i--) + IDE_found[i].ideIOadr = IDE_found[i].ideIOctrladr + = IDE_found[i].irq = IDE_found[i].bios_order + = IDE_found[i].reserved = 0; +#endif + +#if 0 + DBG("togpl = 0x", (unsigned)togpl, ": "); + DBG("signature = 0x", togpl->signature, ", "); + DBG("version = 0x", togpl->version, ", "); + DBG("feature = 0x", togpl->feature, ", "); + DBG("size = 0x", togpl->size, ", "); + DBG("offsetof(struct gpl_compliant_str, DI) + 4 = 0x", + offsetof(struct gpl_compliant_str, DI) + 4, "\r\n"); + DBG_WAIT(); +#endif + + if (togpl == 0 || togpl->signature != 16980327 + || (togpl->version >> 8) != 1 || (togpl->feature & 1) == 0 + || togpl->size <= offsetof(struct gpl_compliant_str, DI) + 4) + return; + + init_IDE_found_from_cmdline (IDE_found, + sizeof(IDE_found)/sizeof(IDE_found[0]), command_line); + init_IDE_found_from_DI (IDE_found, + sizeof(IDE_found)/sizeof(IDE_found[0]), DI); + +#if 0 + { + unsigned cpt; + + for (cpt = 0; cpt < sizeof(IDE_found)/sizeof(IDE_found[0]); cpt++) { + DBG("IDE_found[cpt = 0x", cpt, "] : "); + DBG("ideIOadr = 0x", IDE_found[cpt].ideIOadr, ", "); + DBG("ideIOctrladr = 0x", IDE_found[cpt].ideIOctrladr, ", "); + DBG("irq = 0x", IDE_found[cpt].irq, ", "); + DBG("bios_order = 0x", IDE_found[cpt].bios_order, "\r\n"); + } + DBG_WAIT(); + } +#endif + + /* If "root=" is given, do not try to guess it: */ + for (cmdptr = command_line; *cmdptr != '\0'; cmdptr++) + if (cmdptr[0] == 'r' && cmdptr[1] == 'o' && cmdptr[2] == 'o' + && cmdptr[3] == 't' && cmdptr[4] == '=') + break; + + /* sizeof struct diskparam_str 256, sizeof struct partition_str 64 */ + if (*cmdptr == '\0' + && sizeof (struct diskparam_str) == DI->sizeof_diskparam_str + && sizeof (struct partition_str) == DI->sizeof_partition_str + && DI->param[system_desc->disk].bytepersector != 0) + add_root_parameter (system_desc, cmdptr, IDE_found, + sizeof(IDE_found)/sizeof(IDE_found[0]), + DI, nb_slash_in_kernelname); + + /* Add this after "root=", limited command line length */ + finalise_cmdline (IDE_found, + sizeof(IDE_found)/sizeof(IDE_found[0]), command_line); +} + +#else /* ROOT_EXTENSIVE_SEARCH */ + +struct desc_str; +struct gpl_compliant_str; +#define vmlinuz_ROOT(a, b, c, d) /* nothing */ + +#endif /* ROOT_EXTENSIVE_SEARCH */ + +CODE static inline void +vmlinuz_DISKS (union drive_info *drive1, union drive_info *drive2 +#ifdef ROOT_EXTENSIVE_SEARCH + , const struct disk_interface_str *DI +#endif + ) +{ + unsigned nbsect; + unsigned char status, disk = 0x80; + union drive_info *drive = drive1; + farptr vectoradr = (farptr)(0x41 * 4); + + while (disk <= 0x81) { +#ifdef ROOT_EXTENSIVE_SEARCH + /* Some old and common SCSI card will do an invalid + instruction exception in _BIOSDISK_gettype, if the + BIOS has not been updated */ + int cpt; + for (cpt = 0; cpt < DI->nb_bios_blacklist; cpt++) + if (DI->bios_blacklist[cpt] == disk) + break; + if (cpt < DI->nb_bios_blacklist) + continue; +#endif + + if (_BIOSDISK_gettype (disk, &nbsect, &status) == 0) + get_drive_info (vectoradr, drive); + disk++; + vectoradr = (farptr)(0x46 * 4); + drive = drive2; + } +} + +/** + ** THE function: + **/ +struct LOADER_str { + unsigned short sizeof_loader_str, sizeof_fileload_str; + struct fileload_str { + /* input: */ + unsigned load_address, max_size_or_crc32; + /* only for XMS use: */ + unsigned KBalloced; + unsigned short handle; + /* For reference: */ + unsigned short BOOTWAY_desc_index; + /* output: */ + unsigned compressed_size, uncompressed_size; + } fileload[3], *curfileload; /* kernel and initrd */ + int initrd_index; + unsigned short minor_major_root; + unsigned short license_compliant; + unsigned vmlinuz_byte_treated; /* header + kernel GZIP */ + unsigned uncompressed_signature; + unsigned ramdisk_max; + unsigned accept_uncompressed_initrd_filesize; + unsigned char post_comment_row, post_comment_col; + unsigned short reserved1; + unsigned reserved[5]; + /* Initialised from the "comment" line of the GZIP file */ + union { + unsigned array[32]; + struct { + unsigned NONGPL_productID; + unsigned NONGPL_productNB; + unsigned min_gujin_version; /* 0x100 for 1.0 */ + unsigned late_relocation_address; + unsigned runadr; + unsigned paramadr; /* usually 0x00080000 */ + unsigned realfct_size; /* in bytes */ + unsigned minram; /* in kilobytes */ + unsigned option; + unsigned maskcpu; + unsigned maskDflags; /* request those bits set, + cpuid 0x00000001 in edx (usual) */ + unsigned maskCflags; /* request those bits set, + cpuid 0x00000001 in ecx (new) */ + unsigned maskBflags; /* unused on PCs */ + unsigned maskAflags; /* request those bits set, + cpuid 0x80000001 */ + unsigned maskvesa; /* if bit (8-1) set, + VESA 8BPP supported */ + unsigned maskresolution; /* bit set => exclude + those modes */ + } elem; + } comment; + /* related to Linux version: */ + char cmdline_extraparam[512]; + char cmdline_name[128]; +}; + +CODE __attribute__ ((cdecl, regparm(0))) unsigned +linux_set_params (unsigned local_return_address, + unsigned totalmem, + struct loader_t bootloader_type, + struct linux_param *LnxParam, + const struct LOADER_str *loader, + const struct gpl_compliant_str *togpl, + const struct desc_str *system_desc, + int proposed_row, + char **stringptr, + unsigned char *proposed_cmdline) +{ + unsigned char *ptr; + unsigned nb_slash_in_kernelname = 0; + test_linux_param_struct(); + + vmlinuz_VIDEO (&LnxParam->screen_info, &LnxParam->in_out.vid_mode, + proposed_row); + + /* Just needed for old kernels, before 2.2.x: */ + LnxParam->screen_info.EXT_MEM_K = vmlinuz_EXT_MEM_K(); + + /* old Command Line magic for 2.2- kernels: */ + LnxParam->screen_info.CL_MAGIC = 0xA33F; + LnxParam->screen_info.CL_OFFSET = offsetof(struct linux_param, + command_line); + /* New Command Line magic for 2.4+ kernels: */ + LnxParam->in_out.cmd_line_ptr = loader->comment.elem.paramadr + + offsetof (struct linux_param, command_line); + +/* LnxParam->in_out.header_signature.longword = { { 'H', 'd', 'r', 'S' } }; */ + LnxParam->in_out.header_version_number = 0x0202; + LnxParam->in_out.ramdisk_image = loader->fileload[1].load_address; + LnxParam->in_out.ramdisk_size = loader->fileload[1].uncompressed_size; + LnxParam->in_out.type_of_loader = bootloader_type; + + LnxParam->ALT_MEM_K = totalmem; + + vmlinuz_APM (&LnxParam->apm_bios_info); + + _X86SpeedStepSmi (&LnxParam->X86SpeedStepSmi); + vmlinuz_DISKS (&LnxParam->drive1, &LnxParam->drive2 +#ifdef ROOT_EXTENSIVE_SEARCH + , (const struct disk_interface_str *)togpl->DI +#endif + ); + vmlinuz_CONFIG (&LnxParam->sys_desc_table); + /* do vmlinuz_EDID() before vmlinuz_E820() so that if the later + function describes more than 18 (and less than 32) blocks of memory, + the more important information erases the less important one. + Since 2.6.12 there isn't any overwrite problem.*/ + vmlinuz_EDID (&LnxParam->edid_data); + LnxParam->nb_E820_entries = vmlinuz_E820 (LnxParam->e820map, + sizeof(LnxParam->e820map)); + + /* Now the command line stuff: */ + vmlinuz_CMDLINE (LnxParam->command_line, proposed_cmdline, + *(farptr *)stringptr, + loader->cmdline_name, loader->cmdline_extraparam); + + for (ptr = LnxParam->command_line; *ptr != '\0'; ptr++) { + /* Do not try to be intelligent and do a 32 bits compare, + it will generate constant in .rodata.str1.1 or .rodata + with a version of GCC or another. */ + if (ptr[0] == 'e' && ptr[1] == 'd' && ptr[2] == 'd' + && ptr[3] == '=') + break; + } + /* I do not know about edd=skipmbr, if there is a read error + Gujin will not even boot up to there... */ + if (*ptr == '\0' || ptr[4] != 'o' || ptr[5] != 'f' || ptr[6] != 'f') { + LnxParam->nb_MBRSIG_entries = vmlinuz_MBRSIG (LnxParam->MBRSIG, + sizeof(LnxParam->MBRSIG)/sizeof(LnxParam->MBRSIG[0])); + LnxParam->nb_EDD_entries = vmlinuz_EDD (LnxParam->edd, + sizeof(LnxParam->edd)/sizeof(LnxParam->edd[0])); + } + + for (ptr = LnxParam->command_line; *ptr != '\0'; ptr++) { +#define endparam(c) ((c) == ' ' || (c) == '\n' || (c) == '\t' || (c) == '\0') + if (ptr[0] == 'r' && (ptr[1] == 'w' || ptr[1] == 'o') + && endparam(ptr[2])) { + LnxParam->in_out.root_flags.read_only + = (ptr[1] == 'w')? 0 : 1; + break; + } else { + while (!endparam(*ptr)) + ptr++; + } + } + + if (loader->cmdline_name) + for (ptr = (unsigned char *)loader->cmdline_name; + *ptr != '\0'; + ptr++) + if (*ptr == '/') + nb_slash_in_kernelname++; + vmlinuz_ROOT (system_desc, togpl, LnxParam->command_line, + nb_slash_in_kernelname); + + test_vgaprint(); + + *stringptr = 0; /* Gujin will not print anything by default */ +// *stringptr = LnxParam->command_line; + + return 0; +}