diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/ipxe/src/arch/i386/core | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip | |
Diffstat (limited to 'roms/ipxe/src/arch/i386/core')
20 files changed, 1730 insertions, 0 deletions
| diff --git a/roms/ipxe/src/arch/i386/core/basemem_packet.c b/roms/ipxe/src/arch/i386/core/basemem_packet.c new file mode 100644 index 00000000..06ffa3bb --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/basemem_packet.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Packet buffer in base memory.  Used by various components which + * need to pass packets to and from external real-mode code. + * + */ + +#include <basemem_packet.h> + +#undef basemem_packet +char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] ); diff --git a/roms/ipxe/src/arch/i386/core/cachedhcp.c b/roms/ipxe/src/arch/i386/core/cachedhcp.c new file mode 100644 index 00000000..3cac28e7 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/cachedhcp.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <ipxe/dhcppkt.h> +#include <ipxe/init.h> +#include <ipxe/netdevice.h> +#include <realmode.h> +#include <pxe_api.h> + +/** @file + * + * Cached DHCP packet + * + */ + +/** Cached DHCPACK physical address + * + * This can be set by the prefix. + */ +uint32_t __bss16 ( cached_dhcpack_phys ); +#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys ) + +/** Colour for debug messages */ +#define colour &cached_dhcpack_phys + +/** Cached DHCPACK */ +static struct dhcp_packet *cached_dhcpack; + +/** + * Cached DHCPACK startup function + * + */ +static void cachedhcp_init ( void ) { +	struct dhcp_packet *dhcppkt; +	struct dhcp_packet *tmp; +	struct dhcphdr *dhcphdr; +	size_t len; + +	/* Do nothing if no cached DHCPACK is present */ +	if ( ! cached_dhcpack_phys ) { +		DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" ); +		return; +	} + +	/* No reliable way to determine length before parsing packet; +	 * start by assuming maximum length permitted by PXE. +	 */ +	len = sizeof ( BOOTPLAYER_t ); + +	/* Allocate and populate DHCP packet */ +	dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len ); +	if ( ! dhcppkt ) { +		DBGC ( colour, "CACHEDHCP could not allocate copy\n" ); +		return; +	} +	dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) ); +	copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0, +			 len ); +	dhcppkt_init ( dhcppkt, dhcphdr, len ); + +	/* Resize packet to required length.  If reallocation fails, +	 * just continue to use the original packet. +	 */ +	len = dhcppkt_len ( dhcppkt ); +	tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) ); +	if ( tmp ) +		dhcppkt = tmp; + +	/* Reinitialise packet at new address */ +	dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) ); +	dhcppkt_init ( dhcppkt, dhcphdr, len ); + +	/* Store as cached DHCPACK, and mark original copy as consumed */ +	DBGC ( colour, "CACHEDHCP found cached DHCPACK at %08x+%zx\n", +	       cached_dhcpack_phys, len ); +	cached_dhcpack = dhcppkt; +	cached_dhcpack_phys = 0; +} + +/** + * Cached DHCPACK startup function + * + */ +static void cachedhcp_startup ( void ) { + +	/* If cached DHCP packet was not claimed by any network device +	 * during startup, then free it. +	 */ +	if ( cached_dhcpack ) { +		DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" ); +		dhcppkt_put ( cached_dhcpack ); +		cached_dhcpack = NULL; +	} +} + +/** Cached DHCPACK initialisation function */ +struct init_fn cachedhcp_init_fn __init_fn ( INIT_NORMAL ) = { +	.initialise = cachedhcp_init, +}; + +/** Cached DHCPACK startup function */ +struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = { +	.startup = cachedhcp_startup, +}; + +/** + * Apply cached DHCPACK to network device, if applicable + * + * @v netdev		Network device + * @ret rc		Return status code + */ +static int cachedhcp_probe ( struct net_device *netdev ) { +	struct ll_protocol *ll_protocol = netdev->ll_protocol; +	int rc; + +	/* Do nothing unless we have a cached DHCPACK */ +	if ( ! cached_dhcpack ) +		return 0; + +	/* Do nothing unless cached DHCPACK's MAC address matches this +	 * network device. +	 */ +	if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr, +		      ll_protocol->ll_addr_len ) != 0 ) { +		DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n", +		       netdev->name ); +		return 0; +	} +	DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name ); + +	/* Register as DHCP settings for this network device */ +	if ( ( rc = register_settings ( &cached_dhcpack->settings, +					netdev_settings ( netdev ), +					DHCP_SETTINGS_NAME ) ) != 0 ) { +		DBGC ( colour, "CACHEDHCP could not register settings: %s\n", +		       strerror ( rc ) ); +		return rc; +	} + +	/* Claim cached DHCPACK */ +	dhcppkt_put ( cached_dhcpack ); +	cached_dhcpack = NULL; + +	return 0; +} + +/** Cached DHCP packet network device driver */ +struct net_driver cachedhcp_driver __net_driver = { +	.name = "cachedhcp", +	.probe = cachedhcp_probe, +}; diff --git a/roms/ipxe/src/arch/i386/core/dumpregs.c b/roms/ipxe/src/arch/i386/core/dumpregs.c new file mode 100644 index 00000000..82dc2184 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/dumpregs.c @@ -0,0 +1,23 @@ +#include <stdio.h> +#include <realmode.h> + +void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) { + +	__asm__ __volatile__ ( +		TEXT16_CODE ( ".globl dump_regs\n\t" +			      "\ndump_regs:\n\t" +			      "pushl $_dump_regs\n\t" +			      "pushw %%cs\n\t" +			      "call prot_call\n\t" +			      "addr32 leal 4(%%esp), %%esp\n\t" +			      "ret\n\t" ) : : ); + +	printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" +		 "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" +		 "CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x\n", +		 ix86->regs.eax, ix86->regs.ebx, ix86->regs.ecx, +		 ix86->regs.edx, ix86->regs.esi, ix86->regs.edi, +		 ix86->regs.ebp, ix86->regs.esp, +		 ix86->segs.cs, ix86->segs.ss, ix86->segs.ds, +		 ix86->segs.es, ix86->segs.fs, ix86->segs.gs ); +} diff --git a/roms/ipxe/src/arch/i386/core/gdbidt.S b/roms/ipxe/src/arch/i386/core/gdbidt.S new file mode 100644 index 00000000..a1e309d7 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/gdbidt.S @@ -0,0 +1,140 @@ +/* + * Interrupt handlers for GDB stub + */ + +#define SIZEOF_I386_REGS	32 +#define SIZEOF_I386_FLAGS	4 + +/**************************************************************************** + * Interrupt handlers + **************************************************************************** + */ +	.section ".text", "ax", @progbits +	.code32 + +/* POSIX signal numbers for reporting traps to GDB */ +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGSEGV 11 +#define SIGSTKFLT 16 + +	.globl	gdbmach_nocode_sigfpe +gdbmach_nocode_sigfpe: +	pushl	$SIGFPE +	jmp	gdbmach_interrupt + +	.globl	gdbmach_nocode_sigtrap +gdbmach_nocode_sigtrap: +	pushl	$SIGTRAP +	jmp	gdbmach_interrupt + +	.globl	gdbmach_nocode_sigstkflt +gdbmach_nocode_sigstkflt: +	pushl	$SIGSTKFLT +	jmp	gdbmach_interrupt + +	.globl	gdbmach_nocode_sigill +gdbmach_nocode_sigill: +	pushl	$SIGILL +	jmp	gdbmach_interrupt + +	.globl	gdbmach_withcode_sigbus +gdbmach_withcode_sigbus: +	movl	$SIGBUS, (%esp) +	jmp	gdbmach_interrupt + +	.globl	gdbmach_withcode_sigsegv +gdbmach_withcode_sigsegv: +	movl	$SIGSEGV, (%esp) +	jmp	gdbmach_interrupt + +/* When invoked, the stack contains: eflags, cs, eip, signo. */ +#define IH_OFFSET_GDB_REGS ( 0 ) +#define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS ) +#define IH_OFFSET_GDB_EFLAGS ( IH_OFFSET_GDB_EIP + 4 ) +#define IH_OFFSET_GDB_SEG_REGS ( IH_OFFSET_GDB_EFLAGS + SIZEOF_I386_FLAGS ) +#define IH_OFFSET_GDB_END ( IH_OFFSET_GDB_SEG_REGS + 6 * 4 ) +#define IH_OFFSET_SIGNO ( IH_OFFSET_GDB_END ) +#define IH_OFFSET_OLD_EIP ( IH_OFFSET_SIGNO + 4 ) +#define IH_OFFSET_OLD_CS ( IH_OFFSET_OLD_EIP + 4 ) +#define IH_OFFSET_OLD_EFLAGS ( IH_OFFSET_OLD_CS + 4 ) +#define IH_OFFSET_END ( IH_OFFSET_OLD_EFLAGS + 4 ) + +/* We also access the stack whilst still storing or restoring + * the register snapshot.  Since ESP is in flux, we need + * special offsets. + */ +#define IH_OFFSET_FLUX_OLD_CS ( IH_OFFSET_OLD_CS - 44 ) +#define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 ) +#define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 ) +#define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 ) +gdbmach_interrupt: +	/* Store CPU state in GDB register snapshot */ +	pushw	$0 +	pushw	%gs +	pushw	$0 +	pushw	%fs +	pushw	$0 +	pushw	%es +	pushw	$0 +	pushw	%ds +	pushw	$0 +	pushw	%ss +	pushw	$0 +	pushw	IH_OFFSET_FLUX_OLD_CS + 2(%esp) +	pushl	IH_OFFSET_FLUX_OLD_EFLAGS(%esp) +	pushl	IH_OFFSET_FLUX_OLD_EIP(%esp) +	pushl	%edi +	pushl	%esi +	pushl	%ebp +	leal	IH_OFFSET_FLUX_END(%esp), %edi +	pushl	%edi /* old ESP */ +	pushl	%ebx +	pushl	%edx +	pushl	%ecx +	pushl	%eax + +	/* Switch to virtual addressing */ +	call	_intr_to_virt + +	/* Call GDB stub exception handler */ +	pushl	%esp +	pushl	(IH_OFFSET_SIGNO + 4)(%esp) +	call	gdbmach_handler +	addl	$8, %esp + +	/* Copy register snapshot to new stack and switch to new stack */ +	movl	%esp, %esi +	movl	(IH_OFFSET_GDB_SEG_REGS + 4)(%esp), %eax +	movl	%eax, %es +	movl	(IH_OFFSET_GDB_REGS + 16)(%esp), %edi +	subl	$IH_OFFSET_END, %edi +	movl	$(IH_OFFSET_END / 4), %ecx +	pushl	%edi +	ss rep movsl +	popl	%edi +	movl	%eax, %ss +	movl	%edi, %esp + +	/* Restore CPU state from GDB register snapshot */ +	popl	%eax +	popl	%ecx +	popl	%edx +	popl	%ebx +	popl	%ebp /* Skip %esp: already loaded */ +	popl	%ebp +	popl	%esi +	popl	%edi +	popl	IH_OFFSET_FLUX_OLD_EIP(%esp) +	popl	IH_OFFSET_FLUX_OLD_EFLAGS(%esp) +	popl	IH_OFFSET_FLUX_OLD_CS(%esp) +	popl	%ds /* Skip %ss: already loaded */ +	popl	%ds +	popl	%es +	popl	%fs +	popl	%gs + +	addl	$4, %esp /* drop signo */ +	iret diff --git a/roms/ipxe/src/arch/i386/core/gdbmach.c b/roms/ipxe/src/arch/i386/core/gdbmach.c new file mode 100644 index 00000000..4d6897f7 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/gdbmach.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <stdio.h> +#include <assert.h> +#include <ipxe/uaccess.h> +#include <ipxe/gdbstub.h> +#include <librm.h> +#include <gdbmach.h> + +/** @file + * + * GDB architecture-specific bits for i386 + * + */ + +enum { +	DR7_CLEAR = 0x00000400,    /* disable hardware breakpoints */ +	DR6_CLEAR = 0xffff0ff0,    /* clear breakpoint status */ +}; + +/** Hardware breakpoint, fields stored in x86 bit pattern form */ +struct hwbp { +	int type;           /* type (1=write watchpoint, 3=access watchpoint) */ +	unsigned long addr; /* linear address */ +	size_t len;         /* length (0=1-byte, 1=2-byte, 3=4-byte) */ +	int enabled; +}; + +static struct hwbp hwbps [ 4 ]; +static gdbreg_t dr7 = DR7_CLEAR; + +static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) { +	struct hwbp *available = NULL; +	unsigned int i; +	for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) { +		if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) { +			return &hwbps [ i ]; +		} +		if ( !hwbps [ i ].enabled ) { +			available = &hwbps [ i ]; +		} +	} +	return available; +} + +static void gdbmach_commit_hwbp ( struct hwbp *bp ) { +	unsigned int regnum = bp - hwbps; + +	/* Set breakpoint address */ +	assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) ); +	switch ( regnum ) { +		case 0: +			__asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) ); +			break; +		case 1: +			__asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) ); +			break; +		case 2: +			__asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) ); +			break; +		case 3: +			__asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) ); +			break; +	} + +	/* Set type */ +	dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) ); +	dr7 |= bp->type << ( 16 + 4 * regnum ); + +	/* Set length */ +	dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) ); +	dr7 |= bp->len << ( 18 + 4 * regnum ); + +	/* Set/clear local enable bit */ +	dr7 &= ~( 0x3 << 2 * regnum ); + 	dr7 |= bp->enabled << 2 * regnum; +} + +int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) { +	struct hwbp *bp; +	 +	/* Check and convert breakpoint type to x86 type */ +	switch ( type ) { +		case GDBMACH_WATCH: +			type = 0x1; +			break; +		case GDBMACH_AWATCH: +			type = 0x3; +			break; +		default: +			return 0; /* unsupported breakpoint type */ +	} + +	/* Only lengths 1, 2, and 4 are supported */ +	if ( len != 2 && len != 4 ) { +		len = 1; +	} +	len--; /* convert to x86 breakpoint length bit pattern */ + +	/* Calculate linear address by adding segment base */ +	addr += virt_offset; + +	/* Set up the breakpoint */ +	bp = gdbmach_find_hwbp ( type, addr, len ); +	if ( !bp ) { +		return 0; /* ran out of hardware breakpoints */ +	} +	bp->type = type; +	bp->addr = addr; +	bp->len = len; +	bp->enabled = enable; +	gdbmach_commit_hwbp ( bp ); +	return 1; +} + +static void gdbmach_disable_hwbps ( void ) { +	/* Store and clear hardware breakpoints */ +	__asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) ); +} + +static void gdbmach_enable_hwbps ( void ) { +	/* Clear breakpoint status register */ +	__asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) ); + +	/* Restore hardware breakpoints */ +	__asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) ); +} + +__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) { +	gdbmach_disable_hwbps(); +	gdbstub_handler ( signo, regs ); +	gdbmach_enable_hwbps(); +} + +static void * gdbmach_interrupt_vectors[] = { +	gdbmach_nocode_sigfpe,		/* Divide by zero */ +	gdbmach_nocode_sigtrap,		/* Debug trap */ +	NULL,				/* Non-maskable interrupt */ +	gdbmach_nocode_sigtrap,		/* Breakpoint */ +	gdbmach_nocode_sigstkflt,	/* Overflow */ +	gdbmach_nocode_sigstkflt,	/* Bound range exceeded */ +	gdbmach_nocode_sigill,		/* Invalid opcode */ +	NULL,				/* Device not available */ +	gdbmach_withcode_sigbus,	/* Double fault */ +	NULL,				/* Coprocessor segment overrun */ +	gdbmach_withcode_sigsegv,	/* Invalid TSS */ +	gdbmach_withcode_sigsegv,	/* Segment not present */ +	gdbmach_withcode_sigsegv,	/* Stack segment fault */ +	gdbmach_withcode_sigsegv,	/* General protection fault */ +	gdbmach_withcode_sigsegv,	/* Page fault */ +}; + +void gdbmach_init ( void ) { +	unsigned int i; + +	for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) / +			    sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) { +		set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] ); +	} +} diff --git a/roms/ipxe/src/arch/i386/core/linux/linux_syscall.S b/roms/ipxe/src/arch/i386/core/linux/linux_syscall.S new file mode 100644 index 00000000..38a3e74b --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/linux/linux_syscall.S @@ -0,0 +1,45 @@ + +	.section ".data" +	.globl linux_errno + +linux_errno:	.int 0 + +	.section ".text" +	.code32 +	.globl linux_syscall +	.type  linux_syscall, @function + +linux_syscall: +	/* Save registers */ +	pushl	%ebx +	pushl	%esi +	pushl	%edi +	pushl	%ebp + +	movl	20(%esp), %eax  // C arg1 -> syscall number +	movl	24(%esp), %ebx  // C arg2 -> syscall arg1 +	movl	28(%esp), %ecx  // C arg3 -> syscall arg2 +	movl	32(%esp), %edx  // C arg4 -> syscall arg3 +	movl	36(%esp), %esi  // C arg5 -> syscall arg4 +	movl	40(%esp), %edi  // C arg6 -> syscall arg5 +	movl	44(%esp), %ebp  // C arg7 -> syscall arg6 + +	int	$0x80 + +	/* Restore registers */ +	popl	%ebp +	popl	%edi +	popl	%esi +	popl	%ebx + +	cmpl	$-4095, %eax +	jae	1f +	ret + +1: +	negl	%eax +	movl	%eax, linux_errno +	movl	$-1, %eax +	ret + +	.size linux_syscall, . - linux_syscall diff --git a/roms/ipxe/src/arch/i386/core/linux/linuxprefix.S b/roms/ipxe/src/arch/i386/core/linux/linuxprefix.S new file mode 100644 index 00000000..398d3cb2 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/linux/linuxprefix.S @@ -0,0 +1,28 @@ +#include <linux/unistd.h> + +	.section ".text" +	.code32 +	.globl _linux_start +	.type _linux_start, @function + +_linux_start: +	xorl	%ebp, %ebp + +	popl	%esi       // save argc +	movl	%esp, %edi // save argv + +	andl	$~15, %esp // 16-byte align the stack + +	pushl	%edi // argv -> C arg2 +	pushl	%esi // argc -> C arg1 + +	call	save_args + +	/* Our main doesn't use any arguments */ +	call	main + +	movl	%eax, %ebx // rc -> syscall arg1 +	movl	$__NR_exit, %eax +	int	$0x80 + +	.size _linux_start, . - _linux_start diff --git a/roms/ipxe/src/arch/i386/core/nulltrap.c b/roms/ipxe/src/arch/i386/core/nulltrap.c new file mode 100644 index 00000000..3046fbec --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/nulltrap.c @@ -0,0 +1,51 @@ +#include <stdint.h> +#include <stdio.h> + +__attribute__ (( noreturn, section ( ".text.null_trap" ) )) +void null_function_trap ( void ) { +	void *stack; + +	/* 128 bytes of NOPs; the idea of this is that if something +	 * dereferences a NULL pointer and overwrites us, we at least +	 * have some chance of still getting to execute the printf() +	 * statement. +	 */ +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); +	__asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + +	__asm__ __volatile__ ( "movl %%esp, %0" : "=r" ( stack ) ); +	printf ( "NULL method called from %p (stack %p)\n",  +		 __builtin_return_address ( 0 ), stack ); +	DBG_HD ( stack, 256 ); +	while ( 1 ) {} +} diff --git a/roms/ipxe/src/arch/i386/core/patch_cf.S b/roms/ipxe/src/arch/i386/core/patch_cf.S new file mode 100644 index 00000000..97a62f49 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/patch_cf.S @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 H. Peter Anvin <hpa@zytor.com> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +	.text +	.arch i386 +	.code16 + +/**************************************************************************** + * Set/clear CF on the stack as appropriate, assumes stack is as it should + * be immediately before IRET + **************************************************************************** + */ +	.section ".text16", "ax", @progbits +	.globl patch_cf +patch_cf: +	pushw	%bp +	movw	%sp, %bp +	setc	8(%bp)	/* Set/reset CF; clears PF, AF, ZF, SF */ +	popw	%bp +	ret +	.size patch_cf, . - patch_cf diff --git a/roms/ipxe/src/arch/i386/core/pci_autoboot.c b/roms/ipxe/src/arch/i386/core/pci_autoboot.c new file mode 100644 index 00000000..a3eb1f97 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/pci_autoboot.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 Red Hat Inc. + *	Alex Williamson <alex.williamson@redhat.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <ipxe/device.h> +#include <ipxe/init.h> +#include <realmode.h> +#include <usr/autoboot.h> + +uint16_t __bss16 ( autoboot_busdevfn ); +#define autoboot_busdevfn __use_data16 ( autoboot_busdevfn ) + +/** + * Initialise PCI autoboot device + */ +static void pci_autoboot_init ( void ) { + +	if ( autoboot_busdevfn ) +		set_autoboot_busloc ( BUS_TYPE_PCI, autoboot_busdevfn ); +} + +/** PCI autoboot device initialisation function */ +struct init_fn pci_autoboot_init_fn __init_fn ( INIT_NORMAL ) = { +	.initialise = pci_autoboot_init, +}; diff --git a/roms/ipxe/src/arch/i386/core/pic8259.c b/roms/ipxe/src/arch/i386/core/pic8259.c new file mode 100644 index 00000000..0a9ea2e0 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/pic8259.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ipxe/io.h> +#include <pic8259.h> + +/** @file + * + * Minimal support for the 8259 Programmable Interrupt Controller + * + */ + +/** + * Send non-specific EOI(s) + * + * @v irq		IRQ number + * + * This seems to be inherently unsafe. + */ +static inline void send_nonspecific_eoi ( unsigned int irq ) { +	DBG ( "Sending non-specific EOI for IRQ %d\n", irq ); +	if ( irq >= IRQ_PIC_CUTOFF ) { +		outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR ); +	}		 +	outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR ); +} + +/** + * Send specific EOI(s) + * + * @v irq		IRQ number + */ +static inline void send_specific_eoi ( unsigned int irq ) { +	DBG ( "Sending specific EOI for IRQ %d\n", irq ); +	if ( irq >= IRQ_PIC_CUTOFF ) { +		outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( CHAINED_IRQ ) ), +		       ICR_REG ( CHAINED_IRQ ) ); +	} +	outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( irq ) ), ICR_REG ( irq ) ); +} + +/** + * Send End-Of-Interrupt to the PIC + * + * @v irq		IRQ number + */ +void send_eoi ( unsigned int irq ) { +	send_specific_eoi ( irq ); +} diff --git a/roms/ipxe/src/arch/i386/core/rdtsc_timer.c b/roms/ipxe/src/arch/i386/core/rdtsc_timer.c new file mode 100644 index 00000000..2f31afc6 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/rdtsc_timer.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * RDTSC timer + * + */ + +#include <assert.h> +#include <ipxe/timer.h> +#include <ipxe/timer2.h> + +/** + * Number of TSC ticks per microsecond + * + * This is calibrated on the first use of the timer. + */ +static unsigned long rdtsc_ticks_per_usec; + +/** + * Delay for a fixed number of microseconds + * + * @v usecs		Number of microseconds for which to delay + */ +static void rdtsc_udelay ( unsigned long usecs ) { +	unsigned long start; +	unsigned long elapsed; + +	/* Sanity guard, since we may divide by this */ +	if ( ! usecs ) +		usecs = 1; + +	start = currticks(); +	if ( rdtsc_ticks_per_usec ) { +		/* Already calibrated; busy-wait until done */ +		do { +			elapsed = ( currticks() - start ); +		} while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) ); +	} else { +		/* Not yet calibrated; use timer2 and calibrate +		 * based on result. +		 */ +		timer2_udelay ( usecs ); +		elapsed = ( currticks() - start ); +		rdtsc_ticks_per_usec = ( elapsed / usecs ); +		DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs " +		      "(%ld MHz)\n", elapsed, usecs, +		      ( rdtsc_ticks_per_usec << TSC_SHIFT ) ); +	} +} + +/** + * Get number of ticks per second + * + * @ret ticks_per_sec	Number of ticks per second + */ +static unsigned long rdtsc_ticks_per_sec ( void ) { + +	/* Calibrate timer, if not already done */ +	if ( ! rdtsc_ticks_per_usec ) +		udelay ( 1 ); + +	/* Sanity check */ +	assert ( rdtsc_ticks_per_usec != 0 ); + +	return ( rdtsc_ticks_per_usec * 1000 * 1000 ); +} + +PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay ); +PROVIDE_TIMER_INLINE ( rdtsc, currticks ); +PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec ); diff --git a/roms/ipxe/src/arch/i386/core/relocate.c b/roms/ipxe/src/arch/i386/core/relocate.c new file mode 100644 index 00000000..5fbf2d2c --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/relocate.c @@ -0,0 +1,138 @@ +#include <ipxe/io.h> +#include <registers.h> + +/* + * Originally by Eric Biederman + * + * Heavily modified by Michael Brown  + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/* + * The linker passes in the symbol _max_align, which is the alignment + * that we must preserve, in bytes. + * + */ +extern char _max_align[]; +#define max_align ( ( unsigned int ) _max_align ) + +/* Linker symbols */ +extern char _textdata[]; +extern char _etextdata[]; + +/* within 1MB of 4GB is too close.  + * MAX_ADDR is the maximum address we can easily do DMA to. + * + * Not sure where this constraint comes from, but kept it from Eric's + * old code - mcb30 + */ +#define MAX_ADDR (0xfff00000UL) + +/** + * Relocate iPXE + * + * @v ebp		Maximum address to use for relocation + * @ret esi		Current physical address + * @ret edi		New physical address + * @ret ecx		Length to copy + * + * This finds a suitable location for iPXE near the top of 32-bit + * address space, and returns the physical address of the new location + * to the prefix in %edi. + */ +__asmcall void relocate ( struct i386_all_regs *ix86 ) { +	struct memory_map memmap; +	unsigned long start, end, size, padded_size, max; +	unsigned long new_start, new_end; +	unsigned i; + +	/* Get memory map and current location */ +	get_memmap ( &memmap ); +	start = virt_to_phys ( _textdata ); +	end = virt_to_phys ( _etextdata ); +	size = ( end - start ); +	padded_size = ( size + max_align - 1 ); + +	DBG ( "Relocate: currently at [%lx,%lx)\n" +	      "...need %lx bytes for %d-byte alignment\n", +	      start, end, padded_size, max_align ); + +	/* Determine maximum usable address */ +	max = MAX_ADDR; +	if ( ix86->regs.ebp < max ) { +		max = ix86->regs.ebp; +		DBG ( "Limiting relocation to [0,%lx)\n", max ); +	} + +	/* Walk through the memory map and find the highest address +	 * below 4GB that iPXE will fit into. +	 */ +	new_end = end; +	for ( i = 0 ; i < memmap.count ; i++ ) { +		struct memory_region *region = &memmap.regions[i]; +		unsigned long r_start, r_end; + +		DBG ( "Considering [%llx,%llx)\n", region->start, region->end); +		 +		/* Truncate block to maximum address.  This will be +		 * less than 4GB, which means that we can get away +		 * with using just 32-bit arithmetic after this stage. +		 */ +		if ( region->start > max ) { +			DBG ( "...starts after max=%lx\n", max ); +			continue; +		} +		r_start = region->start; +		if ( region->end > max ) { +			DBG ( "...end truncated to max=%lx\n", max ); +			r_end = max; +		} else { +			r_end = region->end; +		} +		DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end ); + +		/* If we have rounded down r_end below r_ start, skip +		 * this block. +		 */ +		if ( r_end < r_start ) { +			DBG ( "...truncated to negative size\n" ); +			continue; +		} + +		/* Check that there is enough space to fit in iPXE */ +		if ( ( r_end - r_start ) < size ) { +			DBG ( "...too small (need %lx bytes)\n", size ); +			continue; +		} + +		/* If the start address of the iPXE we would +		 * place in this block is higher than the end address +		 * of the current highest block, use this block. +		 * +		 * Note that this avoids overlaps with the current +		 * iPXE, as well as choosing the highest of all viable +		 * blocks. +		 */ +		if ( ( r_end - size ) > new_end ) { +			new_end = r_end; +			DBG ( "...new best block found.\n" ); +		} +	} + +	/* Calculate new location of iPXE, and align it to the +	 * required alignemnt. +	 */ +	new_start = new_end - padded_size; +	new_start += ( start - new_start ) & ( max_align - 1 ); +	new_end = new_start + size; + +	DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n", +	      start, end, new_start, new_end ); +	 +	/* Let prefix know what to copy */ +	ix86->regs.esi = start; +	ix86->regs.edi = new_start; +	ix86->regs.ecx = size; +} diff --git a/roms/ipxe/src/arch/i386/core/runtime.c b/roms/ipxe/src/arch/i386/core/runtime.c new file mode 100644 index 00000000..18ca7936 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/runtime.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Command line and initrd passed to iPXE at runtime + * + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/init.h> +#include <ipxe/image.h> +#include <ipxe/script.h> +#include <ipxe/umalloc.h> +#include <realmode.h> + +/** Command line physical address + * + * This can be set by the prefix. + */ +uint32_t __bss16 ( cmdline_phys ); +#define cmdline_phys __use_data16 ( cmdline_phys ) + +/** initrd physical address + * + * This can be set by the prefix. + */ +uint32_t __bss16 ( initrd_phys ); +#define initrd_phys __use_data16 ( initrd_phys ) + +/** initrd length + * + * This can be set by the prefix. + */ +uint32_t __bss16 ( initrd_len ); +#define initrd_len __use_data16 ( initrd_len ) + +/** Internal copy of the command line */ +static char *cmdline_copy; + +/** Free command line image */ +static void cmdline_image_free ( struct refcnt *refcnt ) { +	struct image *image = container_of ( refcnt, struct image, refcnt ); + +	DBGC ( image, "RUNTIME freeing command line\n" ); +	free ( cmdline_copy ); +} + +/** Embedded script representing the command line */ +static struct image cmdline_image = { +	.refcnt = REF_INIT ( cmdline_image_free ), +	.name = "<CMDLINE>", +	.type = &script_image_type, +}; + +/** Colour for debug messages */ +#define colour &cmdline_image + +/** + * Strip unwanted cruft from command line + * + * @v cmdline		Command line + * @v cruft		Initial substring of cruft to strip + */ +static void cmdline_strip ( char *cmdline, const char *cruft ) { +	char *strip; +	char *strip_end; + +	/* Find unwanted cruft, if present */ +	if ( ! ( strip = strstr ( cmdline, cruft ) ) ) +		return; + +	/* Strip unwanted cruft */ +	strip_end = strchr ( strip, ' ' ); +	if ( strip_end ) { +		*strip_end = '\0'; +		DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip ); +		strcpy ( strip, ( strip_end + 1 ) ); +	} else { +		DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip ); +		*strip = '\0'; +	} +} + +/** + * Initialise command line + * + * @ret rc		Return status code + */ +static int cmdline_init ( void ) { +	userptr_t cmdline_user; +	char *cmdline; +	size_t len; +	int rc; + +	/* Do nothing if no command line was specified */ +	if ( ! cmdline_phys ) { +		DBGC ( colour, "RUNTIME found no command line\n" ); +		return 0; +	} +	cmdline_user = phys_to_user ( cmdline_phys ); +	len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ ); + +	/* Allocate and copy command line */ +	cmdline_copy = malloc ( len ); +	if ( ! cmdline_copy ) { +		DBGC ( colour, "RUNTIME could not allocate %zd bytes for " +		       "command line\n", len ); +		rc = -ENOMEM; +		goto err_alloc_cmdline_copy; +	} +	cmdline = cmdline_copy; +	copy_from_user ( cmdline, cmdline_user, 0, len ); +	DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n", +	       cmdline, cmdline_phys ); + +	/* Mark command line as consumed */ +	cmdline_phys = 0; + +	/* Strip unwanted cruft from the command line */ +	cmdline_strip ( cmdline, "BOOT_IMAGE=" ); +	cmdline_strip ( cmdline, "initrd=" ); +	while ( isspace ( *cmdline ) ) +		cmdline++; +	DBGC ( colour, "RUNTIME using command line \"%s\"\n", cmdline ); + +	/* Prepare and register image */ +	cmdline_image.data = virt_to_user ( cmdline ); +	cmdline_image.len = strlen ( cmdline ); +	if ( cmdline_image.len ) { +		if ( ( rc = register_image ( &cmdline_image ) ) != 0 ) { +			DBGC ( colour, "RUNTIME could not register command " +			       "line: %s\n", strerror ( rc ) ); +			goto err_register_image; +		} +	} + +	/* Drop our reference to the image */ +	image_put ( &cmdline_image ); + +	return 0; + + err_register_image: +	image_put ( &cmdline_image ); + err_alloc_cmdline_copy: +	return rc; +} + +/** + * Initialise initrd + * + * @ret rc		Return status code + */ +static int initrd_init ( void ) { +	struct image *image; +	int rc; + +	/* Do nothing if no initrd was specified */ +	if ( ! initrd_phys ) { +		DBGC ( colour, "RUNTIME found no initrd\n" ); +		return 0; +	} +	if ( ! initrd_len ) { +		DBGC ( colour, "RUNTIME found empty initrd\n" ); +		return 0; +	} +	DBGC ( colour, "RUNTIME found initrd at [%x,%x)\n", +	       initrd_phys, ( initrd_phys + initrd_len ) ); + +	/* Allocate image */ +	image = alloc_image ( NULL ); +	if ( ! image ) { +		DBGC ( colour, "RUNTIME could not allocate image for " +		       "initrd\n" ); +		rc = -ENOMEM; +		goto err_alloc_image; +	} +	if ( ( rc = image_set_name ( image, "<INITRD>" ) ) != 0 ) { +		DBGC ( colour, "RUNTIME could not set image name: %s\n", +		       strerror ( rc ) ); +		goto err_set_name; +	} + +	/* Allocate and copy initrd content */ +	image->data = umalloc ( initrd_len ); +	if ( ! image->data ) { +		DBGC ( colour, "RUNTIME could not allocate %d bytes for " +		       "initrd\n", initrd_len ); +		rc = -ENOMEM; +		goto err_umalloc; +	} +	image->len = initrd_len; +	memcpy_user ( image->data, 0, phys_to_user ( initrd_phys ), 0, +		      initrd_len ); + +	/* Mark initrd as consumed */ +	initrd_phys = 0; + +	/* Register image */ +	if ( ( rc = register_image ( image ) ) != 0 ) { +		DBGC ( colour, "RUNTIME could not register initrd: %s\n", +		       strerror ( rc ) ); +		goto err_register_image; +	} + +	/* Drop our reference to the image */ +	image_put ( image ); + +	return 0; + + err_register_image: + err_umalloc: + err_set_name: +	image_put ( image ); + err_alloc_image: +	return rc; +} + +/** + * Initialise command line and initrd + * + */ +static void runtime_init ( void ) { +	int rc; + +	/* Initialise command line */ +	if ( ( rc = cmdline_init() ) != 0 ) { +		/* No way to report failure */ +		return; +	} + +	/* Initialise initrd */ +	if ( ( rc = initrd_init() ) != 0 ) { +		/* No way to report failure */ +		return; +	} +} + +/** Command line and initrd initialisation function */ +struct startup_fn runtime_startup_fn __startup_fn ( STARTUP_NORMAL ) = { +	.startup = runtime_init, +}; diff --git a/roms/ipxe/src/arch/i386/core/setjmp.S b/roms/ipxe/src/arch/i386/core/setjmp.S new file mode 100644 index 00000000..03727148 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/setjmp.S @@ -0,0 +1,42 @@ +/* setjmp and longjmp. Use of these functions is deprecated. */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +	.text +	.arch i386 +	.code32 +	 +/************************************************************************** +SETJMP - Save stack context for non-local goto +**************************************************************************/ +	.globl	setjmp +setjmp: +	movl	4(%esp),%ecx		/* jmpbuf */ +	movl	0(%esp),%edx		/* return address */ +	movl	%edx,0(%ecx) +	movl	%ebx,4(%ecx) +	movl	%esp,8(%ecx) +	movl	%ebp,12(%ecx) +	movl	%esi,16(%ecx) +	movl	%edi,20(%ecx) +	movl	$0,%eax +	ret + +/************************************************************************** +LONGJMP - Non-local jump to a saved stack context +**************************************************************************/ +	.globl	longjmp +longjmp: +	movl	4(%esp),%edx		/* jumpbuf */ +	movl	8(%esp),%eax		/* result */ +	movl	0(%edx),%ecx +	movl	4(%edx),%ebx +	movl	8(%edx),%esp +	movl	12(%edx),%ebp +	movl	16(%edx),%esi +	movl	20(%edx),%edi +	cmpl	$0,%eax +	jne	1f +	movl	$1,%eax +1:	movl	%ecx,0(%esp) +	ret diff --git a/roms/ipxe/src/arch/i386/core/stack.S b/roms/ipxe/src/arch/i386/core/stack.S new file mode 100644 index 00000000..737ec0ee --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/stack.S @@ -0,0 +1,15 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + +	.arch i386 + +/**************************************************************************** + * Internal stack + **************************************************************************** + */ +	.section ".stack", "aw", @nobits +	.align 8 +	.globl _stack +_stack: +	.space 4096 +	.globl _estack +_estack: diff --git a/roms/ipxe/src/arch/i386/core/stack16.S b/roms/ipxe/src/arch/i386/core/stack16.S new file mode 100644 index 00000000..523f0288 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/stack16.S @@ -0,0 +1,15 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + +	.arch i386 + +/**************************************************************************** + * Internal stack + **************************************************************************** + */ +	.section ".stack16", "aw", @nobits +	.align 8 +	.globl _stack16 +_stack16: +	.space 4096 +	.globl _estack16 +_estack16: diff --git a/roms/ipxe/src/arch/i386/core/timer2.c b/roms/ipxe/src/arch/i386/core/timer2.c new file mode 100644 index 00000000..07786656 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/timer2.c @@ -0,0 +1,87 @@ +/* + * arch/i386/core/i386_timer.c + * + * Use the "System Timer 2" to implement the udelay callback in + * the BIOS timer driver. Also used to calibrate the clock rate + * in the RTDSC timer driver. + *  + * 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, or (at + * your option) any later version. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <ipxe/timer2.h> +#include <ipxe/io.h> + +/* Timers tick over at this rate */ +#define TIMER2_TICKS_PER_SEC	1193180U + +/* Parallel Peripheral Controller Port B */ +#define	PPC_PORTB	0x61 + +/* Meaning of the port bits */ +#define	PPCB_T2OUT	0x20	/* Bit 5 */ +#define	PPCB_SPKR	0x02	/* Bit 1 */ +#define	PPCB_T2GATE	0x01	/* Bit 0 */ + +/* Ports for the 8254 timer chip */ +#define	TIMER2_PORT	0x42 +#define	TIMER_MODE_PORT	0x43 + +/* Meaning of the mode bits */ +#define	TIMER0_SEL	0x00 +#define	TIMER1_SEL	0x40 +#define	TIMER2_SEL	0x80 +#define	READBACK_SEL	0xC0 + +#define	LATCH_COUNT	0x00 +#define	LOBYTE_ACCESS	0x10 +#define	HIBYTE_ACCESS	0x20 +#define	WORD_ACCESS	0x30 + +#define	MODE0		0x00 +#define	MODE1		0x02 +#define	MODE2		0x04 +#define	MODE3		0x06 +#define	MODE4		0x08 +#define	MODE5		0x0A + +#define	BINARY_COUNT	0x00 +#define	BCD_COUNT	0x01 + +static void load_timer2 ( unsigned int ticks ) { +	/* +	 * Now let's take care of PPC channel 2 +	 * +	 * Set the Gate high, program PPC channel 2 for mode 0, +	 * (interrupt on terminal count mode), binary count, +	 * load 5 * LATCH count, (LSB and MSB) to begin countdown. +	 * +	 * Note some implementations have a bug where the high bits byte +	 * of channel 2 is ignored. +	 */ +	/* Set up the timer gate, turn off the speaker */ +	/* Set the Gate high, disable speaker */ +	outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); +	/* binary, mode 0, LSB/MSB, Ch 2 */ +	outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); +	/* LSB of ticks */ +	outb(ticks & 0xFF, TIMER2_PORT); +	/* MSB of ticks */ +	outb(ticks >> 8, TIMER2_PORT); +} + +static int timer2_running ( void ) { +	return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); +} + +void timer2_udelay ( unsigned long usecs ) { +	load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) ); +	while (timer2_running()) { +		/* Do nothing */ +	} +} diff --git a/roms/ipxe/src/arch/i386/core/video_subr.c b/roms/ipxe/src/arch/i386/core/video_subr.c new file mode 100644 index 00000000..3f701bd9 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/video_subr.c @@ -0,0 +1,113 @@ +/* + * + * modified from linuxbios code + * by Cai Qiang <rimy2000@hotmail.com> + * + */ + +#include "stddef.h" +#include "string.h" +#include <ipxe/io.h> +#include <ipxe/console.h> +#include <ipxe/init.h> +#include "vga.h" +#include <config/console.h> + +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_DIRECT_VGA ) && \ +	CONSOLE_EXPLICIT ( CONSOLE_DIRECT_VGA ) ) +#undef CONSOLE_DIRECT_VGA +#define CONSOLE_DIRECT_VGA ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) +#endif + +struct console_driver vga_console __console_driver; + +static char *vidmem;		/* The video buffer */ +static int video_line, video_col; + +#define VIDBUFFER 0xB8000	 + +static void memsetw(void *s, int c, unsigned int n) +{ +	unsigned int i; +	u16 *ss = (u16 *) s; + +	for (i = 0; i < n; i++) { +		ss[i] = ( u16 ) c; +	} +} + +static void video_init(void) +{ +	static int inited=0; + +	vidmem = (char *)phys_to_virt(VIDBUFFER); + +	if (!inited) { +		video_line = 0; +		video_col = 0; +	 +	 	memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); // + +		inited=1; +	} +} + +static void video_scroll(void) +{ +	int i; + +	memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2); +	for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2) +		vidmem[i] = ' '; +} + +static void vga_putc(int byte) +{ +	if (byte == '\n') { +		video_line++; +		video_col = 0; + +	} else if (byte == '\r') { +		video_col = 0; + +	} else if (byte == '\b') { +		video_col--; + +	} else if (byte == '\t') { +		video_col += 4; + +	} else if (byte == '\a') { +		//beep +		//beep(500); + +	} else { +		vidmem[((video_col + (video_line *COLS)) * 2)] = byte; +		vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT; +		video_col++; +	} +	if (video_col < 0) { +		video_col = 0; +	} +	if (video_col >= COLS) { +		video_line++; +		video_col = 0; +	} +	if (video_line >= LINES) { +		video_scroll(); +		video_line--; +	} +	// move the cursor +	write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI); +	write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO); +} + +struct console_driver vga_console __console_driver = { +	.putchar = vga_putc, +	.disabled = CONSOLE_DISABLED, +	.usage = CONSOLE_DIRECT_VGA, +}; + +struct init_fn video_init_fn __init_fn ( INIT_EARLY ) = { +	.initialise = video_init, +}; diff --git a/roms/ipxe/src/arch/i386/core/virtaddr.S b/roms/ipxe/src/arch/i386/core/virtaddr.S new file mode 100644 index 00000000..5e5d7735 --- /dev/null +++ b/roms/ipxe/src/arch/i386/core/virtaddr.S @@ -0,0 +1,145 @@ +/* + * Functions to support the virtual addressing method of relocation + * that Etherboot uses. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +#include "librm.h" +		 +	.arch i386 +	.text +	.code32 +	 +/**************************************************************************** + * _virt_to_phys (virtual addressing) + * + * Switch from virtual to flat physical addresses.  %esp is adjusted + * to a physical value.  Segment registers are set to flat physical + * selectors.  All other registers are preserved.  Flags are + * preserved. + * + * Parameters: none + * Returns: none + **************************************************************************** + */ +	.globl _virt_to_phys +_virt_to_phys: +	/* Preserve registers and flags */ +	pushfl +	pushl	%eax +	pushl	%ebp + +	/* Change return address to a physical address */ +	movl	virt_offset, %ebp +	addl	%ebp, 12(%esp) + +	/* Switch to physical code segment */ +	cli +	pushl	$PHYSICAL_CS +	leal	1f(%ebp), %eax +	pushl	%eax +	lret +1: +	/* Reload other segment registers and adjust %esp */ +	movl	$PHYSICAL_DS, %eax +	movl	%eax, %ds +	movl	%eax, %es +	movl	%eax, %fs +	movl	%eax, %gs +	movl	%eax, %ss +	addl	%ebp, %esp + +	/* Restore registers and flags, and return */ +	popl	%ebp +	popl	%eax +	popfl +	ret + +/**************************************************************************** + * _phys_to_virt (flat physical addressing) + * + * Switch from flat physical to virtual addresses.  %esp is adjusted + * to a virtual value.  Segment registers are set to virtual + * selectors.  All other registers are preserved.  Flags are + * preserved. + * + * Parameters: none + * Returns: none + **************************************************************************** + */ +	.globl _phys_to_virt +_phys_to_virt: +	/* Preserve registers and flags */ +	pushfl +	pushl	%eax +	pushl	%ebp + +	/* Switch to virtual code segment */ +	cli +	ljmp	$VIRTUAL_CS, $1f +1: +	/* Reload data segment registers */ +	movl	$VIRTUAL_DS, %eax +	movl	%eax, %ds +	movl	%eax, %es +	movl	%eax, %fs +	movl	%eax, %gs + +	/* Reload stack segment and adjust %esp */ +	movl	virt_offset, %ebp +	movl	%eax, %ss +	subl	%ebp, %esp + +	/* Change the return address to a virtual address */ +	subl	%ebp, 12(%esp) + +	/* Restore registers and flags, and return */ +	popl	%ebp +	popl	%eax +	popfl +	ret + +/**************************************************************************** + * _intr_to_virt (virtual code segment, virtual or physical stack segment) + * + * Switch from virtual code segment with either a virtual or physical + * stack segment to using virtual addressing.  %esp is adjusted if + * necessary to a virtual value.  Segment registers are set to virtual + * selectors.  All other registers are preserved.  Flags are + * preserved. + * + * Parameters: none + * Returns: none + **************************************************************************** + */ +	.globl _intr_to_virt +_intr_to_virt: +	/* Preserve registers and flags */ +	pushfl +	pushl	%eax +	pushl	%ebp + +	/* Check whether stack segment is physical or virtual */ +	movl	%ss, %eax +	cmpw	$VIRTUAL_DS, %ax +	movl	$VIRTUAL_DS, %eax + +	/* Reload data segment registers */ +	movl	%eax, %ds +	movl	%eax, %es +	movl	%eax, %fs +	movl	%eax, %gs + +	/* Reload stack segment and adjust %esp if necessary */ +	je	1f +	movl	virt_offset, %ebp +	movl	%eax, %ss +	subl	%ebp, %esp +1: +	/* Restore registers and flags, and return */ +	popl	%ebp +	popl	%eax +	popfl +	ret | 
