/* * Example of use of user mode libqemu: launch a basic .com DOS * executable */ #include #include #include #include #include #include #include #include #include #include "cpu.h" //#define SIGTEST void cpu_outb(CPUState *env, int addr, int val) { fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val); } void cpu_outw(CPUState *env, int addr, int val) { fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val); } void cpu_outl(CPUState *env, int addr, int val) { fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val); } int cpu_inb(CPUState *env, int addr) { fprintf(stderr, "inb: port=0x%04x\n", addr); return 0; } int cpu_inw(CPUState *env, int addr) { fprintf(stderr, "inw: port=0x%04x\n", addr); return 0; } int cpu_inl(CPUState *env, int addr) { fprintf(stderr, "inl: port=0x%04x\n", addr); return 0; } int cpu_get_pic_interrupt(CPUState *env) { return -1; } uint64_t cpu_get_tsc(CPUState *env) { return 0; } static void set_gate(void *ptr, unsigned int type, unsigned int dpl, unsigned long addr, unsigned int sel) { unsigned int e1, e2; e1 = (addr & 0xffff) | (sel << 16); e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); stl((uint8_t *)ptr, e1); stl((uint8_t *)ptr + 4, e2); } uint64_t idt_table[256]; /* only dpl matters as we do only user space emulation */ static void set_idt(int n, unsigned int dpl) { set_gate(idt_table + n, 0, dpl, 0, 0); } void qemu_free(void *ptr) { free(ptr); } void *qemu_malloc(size_t size) { return malloc(size); } void *qemu_mallocz(size_t size) { void *ptr; ptr = qemu_malloc(size); if (!ptr) return NULL; memset(ptr, 0, size); return ptr; } void *qemu_vmalloc(size_t size) { return memalign(4096, size); } void qemu_vfree(void *ptr) { free(ptr); } void qemu_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } /* XXX: this is a bug in helper2.c */ int errno; /**********************************************/ #define COM_BASE_ADDR 0x10100 void usage(void) { printf("qruncom version 0.1 (c) 2003 Fabrice Bellard\n" "usage: qruncom file.com\n" "user mode libqemu demo: run simple .com DOS executables\n"); exit(1); } static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) { return (uint8_t *)((seg << 4) + (reg & 0xffff)); } static inline void pushw(CPUState *env, int val) { env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | ((env->regs[R_ESP] - 2) & 0xffff); *(uint16_t *)seg_to_linear(env->segs[R_SS].selector, env->regs[R_ESP]) = val; } static void host_segv_handler(int host_signum, siginfo_t *info, void *puc) { if (cpu_signal_handler(host_signum, info, puc)) { return; } abort(); } int main(int argc, char **argv) { uint8_t *vm86_mem; const char *filename; int fd, ret, seg; CPUState *env; if (argc != 2) usage(); filename = argv[1]; vm86_mem = mmap((void *)0x00000000, 0x110000, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); if (vm86_mem == MAP_FAILED) { perror("mmap"); exit(1); } /* load the MSDOS .com executable */ fd = open(filename, O_RDONLY); if (fd < 0) { perror(filename); exit(1); } ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256); if (ret < 0) { perror("read"); exit(1); } close(fd); /* install exception handler for CPU emulator */ { struct sigaction act; sigfillset(&act.sa_mask); act.sa_flags = SA_SIGINFO; // act.sa_flags |= SA_ONSTACK; act.sa_sigaction = host_segv_handler; sigaction(SIGSEGV, &act, NULL); sigaction(SIGBUS, &act, NULL); #if defined (TARGET_I386) && defined(USE_CODE_COPY) sigaction(SIGFPE, &act, NULL); #endif } // cpu_set_log(CPU_LOG_TB_IN_ASM | CPU_LOG_TB_OUT_ASM | CPU_LOG_EXEC); env = cpu_init(); /* disable code copy to simplify debugging */ code_copy_enabled = 0; /* set user mode state (XXX: should be done automatically by cpu_init ?) */ env->user_mode_only = 1; cpu_x86_set_cpl(env, 3); env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; /* NOTE: hflags duplicates some of the virtual CPU state */ env->hflags |= HF_PE_MASK | VM_MASK; /* flags setup : we activate the IRQs by default as in user mode. We also activate the VM86 flag to run DOS code */ env->eflags |= IF_MASK | VM_MASK; /* init basic registers */ env->eip = 0x100; env->regs[R_ESP] = 0xfffe; seg = (COM_BASE_ADDR - 0x100) >> 4; cpu_x86_load_seg_cache(env, R_CS, seg, (seg << 4), 0xffff, 0); cpu_x86_load_seg_cache(env, R_SS, seg, (seg << 4), 0xffff, 0); cpu_x86_load_seg_cache(env, R_DS, seg, (seg << 4), 0xffff, 0); cpu_x86_load_seg_cache(env, R_ES, seg, (seg << 4), 0xffff, 0); cpu_x86_load_seg_cache(env, R_FS, seg, (seg << 4), 0xffff, 0); cpu_x86_load_seg_cache(env, R_GS, seg, (seg << 4), 0xffff, 0); /* exception support */ env->idt.base = (unsigned long)idt_table; env->idt.limit = sizeof(idt_table) - 1; set_idt(0, 0); set_idt(1, 0); set_idt(2, 0); set_idt(3, 3); set_idt(4, 3); set_idt(5, 3); set_idt(6, 0); set_idt(7, 0); set_idt(8, 0); set_idt(9, 0); set_idt(10, 0); set_idt(11, 0); set_idt(12, 0); set_idt(13, 0); set_idt(14, 0); set_idt(15, 0); set_idt(16, 0); set_idt(17, 0); set_idt(18, 0); set_idt(19, 0); /* put return code */ *seg_to_linear(env->segs[R_CS].selector, 0) = 0xb4; /* mov ah, $0 */ *seg_to_linear(env->segs[R_CS].selector, 1) = 0x00; *seg_to_linear(env->segs[R_CS].selector, 2) = 0xcd; /* int $0x21 */ *seg_to_linear(env->segs[R_CS].selector, 3) = 0x21; pushw(env, 0x0000); /* the value of these registers seem to be assumed by pi_10.com */ env->regs[R_ESI] = 0x100; env->regs[R_ECX] = 0xff; env->regs[R_EBP] = 0x0900; env->regs[R_EDI] = 0xfffe; /* inform the emulator of the mmaped memory */ page_set_flags(0x00000000, 0x110000, PAGE_WRITE | PAGE_READ | PAGE_EXEC | PAGE_VALID); for(;;) { ret = cpu_x86_exec(env); switch(ret) { case EXCP0D_GPF: { int int_num, ah; int_num = *(uint8_t *)(env->segs[R_CS].base + env->eip + 1); if (int_num != 0x21) goto unknown_int; ah = (env->regs[R_EAX] >> 8) & 0xff; switch(ah) { case 0x00: /* exit */ exit(0); case 0x02: /* write char */ { uint8_t c = env->regs[R_EDX]; write(1, &c, 1); } break; case 0x09: /* write string */ { uint8_t c; for(;;) { c = *seg_to_linear(env->segs[R_DS].selector, env->regs[R_EAX]); if (c == '$') break; write(1, &c, 1); } env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | '$'; } break; default: unknown_int: fprintf(stderr, "unsupported int 0x%02x\n", int_num); cpu_dump_state(env, stderr, fprintf, 0); // exit(1); } env->eip += 2; } break; default: fprintf(stderr, "unhandled cpu_exec return code (0x%x)\n", ret); cpu_dump_state(env, stderr, fprintf, 0); exit(1); } } } ' href='#n121'>121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
#ifndef __LINUX_NETLINK_H
#define __LINUX_NETLINK_H

/**
 * Netlink socket address
 * @ingroup nl
 */
struct sockaddr_nl
{
	/** socket family (AF_NETLINK) */
	sa_family_t     nl_family;

	/** Padding (unused) */
	unsigned short  nl_pad;

	/** Unique process ID  */
	uint32_t        nl_pid;

	/** Multicast group subscriptions */
	uint32_t        nl_groups;
};

/**
 * Netlink message header
 * @ingroup msg
 */
struct nlmsghdr
{
	/**
	 * Length of message including header.
	 */
	uint32_t	nlmsg_len;

	/**
	 * Message type (content type)
	 */
	uint16_t	nlmsg_type;

	/**
	 * Message flags
	 */
	uint16_t	nlmsg_flags;

	/**
	 * Sequence number
	 */
	uint32_t	nlmsg_seq;

	/**
	 * Netlink PID of the proccess sending the message.
	 */
	uint32_t	nlmsg_pid;
};

/**
 * @name Standard message flags
 * @{
 */

/**
 * Must be set on all request messages (typically from user space to
 * kernel space).
 * @ingroup msg
 */
#define NLM_F_REQUEST		1

/**
 * Indicates the message is part of a multipart message terminated
 * by NLMSG_DONE.
 */
#define NLM_F_MULTI		2

/**
 * Request for an acknowledgment on success.
 */
#define NLM_F_ACK		4

/**
 * Echo this request
 */
#define NLM_F_ECHO		8

/** @} */

/**
 * @name Additional message flags for GET requests
 * @{
 */

/**
 * Return the complete table instead of a single entry.
 * @ingroup msg
 */
#define NLM_F_ROOT	0x100

/**
 * Return all entries matching criteria passed in message content.
 */
#define NLM_F_MATCH	0x200

/**
 * Return an atomic snapshot of the table being referenced. This
 * may require special privileges because it has the potential to
 * interrupt service in the FE for a longer time.
 */
#define NLM_F_ATOMIC	0x400

/**
 * Dump all entries
 */
#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)

/** @} */

/**
 * @name Additional messsage flags for NEW requests
 * @{
 */

/**
 * Replace existing matching config object with this request.
 * @ingroup msg
 */
#define NLM_F_REPLACE	0x100

/**
 * Don't replace the config object if it already exists.
 */
#define NLM_F_EXCL	0x200

/**
 * Create config object if it doesn't already exist.
 */
#define NLM_F_CREATE	0x400

/**
 * Add to the end of the object list.
 */
#define NLM_F_APPEND	0x800

/** @} */

/**
 * @name Standard Message types
 * @{
 */

/**
 * No operation, message must be ignored
 * @ingroup msg
 */
#define NLMSG_NOOP		0x1

/**
 * The message signals an error and the payload contains a nlmsgerr
 * structure. This can be looked at as a NACK and typically it is
 * from FEC to CPC.
 */
#define NLMSG_ERROR		0x2

/**
 * Message terminates a multipart message.
 */
#define NLMSG_DONE		0x3

/**
 * The message signals that data got lost
 */
#define NLMSG_OVERRUN		0x4

/**
 * Lower limit of reserved message types
 */
#define NLMSG_MIN_TYPE		0x10

/** @} */

/**
 * Netlink error message
 * @ingroup msg
 */
struct nlmsgerr
{
	/** Error code (errno number) */
	int		error;

	/** Original netlink message causing the error */
	struct nlmsghdr	msg;
};

struct nl_pktinfo
{
	__u32	group;
};

#endif	/* __LINUX_NETLINK_H */