start_up.c 15.4 KB
Newer Older
1
/*
L
Linus Torvalds 已提交
2 3 4 5
 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
 * Licensed under the GPL
 */

6
#include <pty.h>
L
Linus Torvalds 已提交
7
#include <stdio.h>
8 9 10 11
#include <stddef.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
L
Linus Torvalds 已提交
12 13 14
#include <unistd.h>
#include <signal.h>
#include <sched.h>
15
#include <fcntl.h>
L
Linus Torvalds 已提交
16 17 18 19 20 21
#include <errno.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <asm/unistd.h>
#include <asm/page.h>
22
#include <sys/types.h>
L
Linus Torvalds 已提交
23 24 25 26 27 28 29 30
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "signal_kern.h"
#include "sysdep/ptrace.h"
#include "sysdep/sigcontext.h"
#include "irq_user.h"
#include "ptrace_user.h"
31
#include "mem_user.h"
L
Linus Torvalds 已提交
32 33 34 35 36
#include "init.h"
#include "os.h"
#include "uml-config.h"
#include "choose-mode.h"
#include "mode.h"
37
#include "tempfile.h"
38 39
#include "kern_constants.h"

L
Linus Torvalds 已提交
40 41 42 43 44 45
#ifdef UML_CONFIG_MODE_SKAS
#include "skas.h"
#include "skas_ptrace.h"
#include "registers.h"
#endif

46
static int ptrace_child(void *arg)
L
Linus Torvalds 已提交
47 48 49 50 51
{
	int ret;
	int pid = os_getpid(), ppid = getppid();
	int sc_result;

52
	change_sig(SIGWINCH, 0);
L
Linus Torvalds 已提交
53 54 55 56
	if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
		perror("ptrace");
		os_kill_process(pid, 0);
	}
57
	kill(pid, SIGSTOP);
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

	/*This syscall will be intercepted by the parent. Don't call more than
	 * once, please.*/
	sc_result = os_getpid();

	if (sc_result == pid)
		ret = 1; /*Nothing modified by the parent, we are running
			   normally.*/
	else if (sc_result == ppid)
		ret = 0; /*Expected in check_ptrace and check_sysemu when they
			   succeed in modifying the stack frame*/
	else
		ret = 2; /*Serious trouble! This could be caused by a bug in
			   host 2.6 SKAS3/2.6 patch before release -V6, together
			   with a bug in the UML code itself.*/
	_exit(ret);
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
static void fatal_perror(char *str)
{
	perror(str);
	exit(1);
}

static void fatal(char *fmt, ...)
{
	va_list list;

	va_start(list, fmt);
	vprintf(fmt, list);
	va_end(list);
	fflush(stdout);

	exit(1);
}

static void non_fatal(char *fmt, ...)
{
	va_list list;

	va_start(list, fmt);
	vprintf(fmt, list);
	va_end(list);
	fflush(stdout);
}

104
static int start_ptraced_child(void **stack_out)
L
Linus Torvalds 已提交
105
{
106 107
	void *stack;
	unsigned long sp;
L
Linus Torvalds 已提交
108
	int pid, n, status;
109

110 111 112
	stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if(stack == MAP_FAILED)
113
		fatal_perror("check_ptrace : mmap failed");
114 115
	sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
	pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL);
L
Linus Torvalds 已提交
116
	if(pid < 0)
117
		fatal_perror("start_ptraced_child : clone failed");
L
Linus Torvalds 已提交
118 119
	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
	if(n < 0)
120
		fatal_perror("check_ptrace : clone failed");
L
Linus Torvalds 已提交
121
	if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
122
		fatal("check_ptrace : expected SIGSTOP, got status = %d",
L
Linus Torvalds 已提交
123 124
		      status);

125
	*stack_out = stack;
126
	return pid;
L
Linus Torvalds 已提交
127 128
}

129 130 131
/* When testing for SYSEMU support, if it is one of the broken versions, we
 * must just avoid using sysemu, not panic, but only if SYSEMU features are
 * broken.
L
Linus Torvalds 已提交
132
 * So only for SYSEMU features we test mustpanic, while normal host features
133 134 135
 * must work anyway!
 */
static int stop_ptraced_child(int pid, void *stack, int exitcode,
136
			      int mustexit)
L
Linus Torvalds 已提交
137 138 139 140
{
	int status, n, ret = 0;

	if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
141
		fatal_perror("stop_ptraced_child : ptrace failed");
L
Linus Torvalds 已提交
142 143 144 145
	CATCH_EINTR(n = waitpid(pid, &status, 0));
	if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
		int exit_with = WEXITSTATUS(status);
		if (exit_with == 2)
146 147 148 149 150 151 152 153 154
			non_fatal("check_ptrace : child exited with status 2. "
				  "Serious trouble happening! Try updating "
				  "your host skas patch!\nDisabling SYSEMU "
				  "support.");
		non_fatal("check_ptrace : child exited with exitcode %d, while "
			  "expecting %d; status 0x%x\n", exit_with,
			  exitcode, status);
		if (mustexit)
			exit(1);
L
Linus Torvalds 已提交
155 156 157
		ret = -1;
	}

158
	if(munmap(stack, PAGE_SIZE) < 0)
159
		fatal_perror("check_ptrace : munmap failed");
L
Linus Torvalds 已提交
160 161 162
	return ret;
}

163
/* Changed only during early boot */
164
int ptrace_faultinfo = 1;
165
int ptrace_ldt = 1;
166
int proc_mm = 1;
167
int skas_needs_stub = 0;
168 169 170 171 172 173 174

static int __init skas0_cmd_param(char *str, int* add)
{
	ptrace_faultinfo = proc_mm = 0;
	return 0;
}

175 176 177 178 179
/* The two __uml_setup would conflict, without this stupid alias. */

static int __init mode_skas0_cmd_param(char *str, int* add)
	__attribute__((alias("skas0_cmd_param")));

180 181 182 183 184
__uml_setup("skas0", skas0_cmd_param,
		"skas0\n"
		"    Disables SKAS3 usage, so that SKAS0 is used, unless \n"
	        "    you specify mode=tt.\n\n");

185 186 187 188 189 190
__uml_setup("mode=skas0", mode_skas0_cmd_param,
		"mode=skas0\n"
		"    Disables SKAS3 usage, so that SKAS0 is used, unless you \n"
		"    specify mode=tt. Note that this was recently added - on \n"
		"    older kernels you must use simply \"skas0\".\n\n");

191
/* Changed only during early boot */
192 193
static int force_sysemu_disabled = 0;

L
Linus Torvalds 已提交
194 195 196 197 198 199 200
static int __init nosysemu_cmd_param(char *str, int* add)
{
	force_sysemu_disabled = 1;
	return 0;
}

__uml_setup("nosysemu", nosysemu_cmd_param,
201 202 203 204 205 206 207
"nosysemu\n"
"    Turns off syscall emulation patch for ptrace (SYSEMU) on.\n"
"    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
"    behaviour of ptrace() and helps reducing host context switch rate.\n"
"    To make it working, you need a kernel patch for your host, too.\n"
"    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
"    information.\n\n");
L
Linus Torvalds 已提交
208 209 210

static void __init check_sysemu(void)
{
211
	void *stack;
212
	int pid, n, status, count=0;
L
Linus Torvalds 已提交
213

214
	non_fatal("Checking syscall emulation patch for ptrace...");
L
Linus Torvalds 已提交
215
	sysemu_supported = 0;
216
	pid = start_ptraced_child(&stack);
L
Linus Torvalds 已提交
217 218 219 220 221 222

	if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
		goto fail;

	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
	if (n < 0)
223
		fatal_perror("check_sysemu : wait failed");
L
Linus Torvalds 已提交
224
	if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
225 226
		fatal("check_sysemu : expected SIGTRAP, got status = %d",
		      status);
L
Linus Torvalds 已提交
227 228 229 230

	n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
		   os_getpid());
	if(n < 0)
231 232
		fatal_perror("check_sysemu : failed to modify system call "
			     "return");
L
Linus Torvalds 已提交
233

234
	if (stop_ptraced_child(pid, stack, 0, 0) < 0)
L
Linus Torvalds 已提交
235 236 237
		goto fail_stopped;

	sysemu_supported = 1;
238
	non_fatal("OK\n");
L
Linus Torvalds 已提交
239 240
	set_using_sysemu(!force_sysemu_disabled);

241
	non_fatal("Checking advanced syscall emulation patch for ptrace...");
242
	pid = start_ptraced_child(&stack);
243

244 245 246
	if((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
		   (void *) PTRACE_O_TRACESYSGOOD) < 0))
		fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
247

L
Linus Torvalds 已提交
248 249 250 251 252 253
	while(1){
		count++;
		if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
			goto fail;
		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
		if(n < 0)
254 255
			fatal_perror("check_ptrace : wait failed");

256
		if(WIFSTOPPED(status) && (WSTOPSIG(status) == (SIGTRAP|0x80))){
L
Linus Torvalds 已提交
257
			if (!count)
258
				fatal("check_ptrace : SYSEMU_SINGLESTEP "
259
				      "doesn't singlestep");
L
Linus Torvalds 已提交
260 261 262
			n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
				   os_getpid());
			if(n < 0)
263 264
				fatal_perror("check_sysemu : failed to modify "
					     "system call return");
L
Linus Torvalds 已提交
265 266
			break;
		}
267 268 269
		else if(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
			count++;
		else
270 271
			fatal("check_ptrace : expected SIGTRAP or "
			      "(SIGTRAP | 0x80), got status = %d", status);
L
Linus Torvalds 已提交
272
	}
273
	if (stop_ptraced_child(pid, stack, 0, 0) < 0)
L
Linus Torvalds 已提交
274 275 276
		goto fail_stopped;

	sysemu_supported = 2;
277
	non_fatal("OK\n");
L
Linus Torvalds 已提交
278 279 280 281 282 283

	if ( !force_sysemu_disabled )
		set_using_sysemu(sysemu_supported);
	return;

fail:
284
	stop_ptraced_child(pid, stack, 1, 0);
L
Linus Torvalds 已提交
285
fail_stopped:
286
	non_fatal("missing\n");
L
Linus Torvalds 已提交
287 288
}

289
static void __init check_ptrace(void)
L
Linus Torvalds 已提交
290
{
291
	void *stack;
L
Linus Torvalds 已提交
292 293
	int pid, syscall, n, status;

294
	non_fatal("Checking that ptrace can change system call numbers...");
295
	pid = start_ptraced_child(&stack);
L
Linus Torvalds 已提交
296

297 298 299
	if((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
		   (void *) PTRACE_O_TRACESYSGOOD) < 0))
		fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
L
Linus Torvalds 已提交
300 301 302

	while(1){
		if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
303 304
			fatal_perror("check_ptrace : ptrace failed");

L
Linus Torvalds 已提交
305 306
		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
		if(n < 0)
307 308 309 310 311 312
			fatal_perror("check_ptrace : wait failed");

		if(!WIFSTOPPED(status) ||
		   (WSTOPSIG(status) != (SIGTRAP | 0x80)))
			fatal("check_ptrace : expected (SIGTRAP|0x80), "
			       "got status = %d", status);
313

L
Linus Torvalds 已提交
314 315 316 317 318 319
		syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET,
				 0);
		if(syscall == __NR_getpid){
			n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
				   __NR_getppid);
			if(n < 0)
320 321
				fatal_perror("check_ptrace : failed to modify "
					     "system call");
L
Linus Torvalds 已提交
322 323 324
			break;
		}
	}
325
	stop_ptraced_child(pid, stack, 0, 1);
326
	non_fatal("OK\n");
L
Linus Torvalds 已提交
327 328 329
	check_sysemu();
}

330
extern void check_tmpexec(void);
331

332
void os_early_checks(void)
L
Linus Torvalds 已提交
333
{
334
	check_ptrace();
335 336 337 338 339

	/* Need to check this early because mmapping happens before the
	 * kernel is running.
	 */
	check_tmpexec();
L
Linus Torvalds 已提交
340 341
}

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
static int __init noprocmm_cmd_param(char *str, int* add)
{
	proc_mm = 0;
	return 0;
}

__uml_setup("noprocmm", noprocmm_cmd_param,
"noprocmm\n"
"    Turns off usage of /proc/mm, even if host supports it.\n"
"    To support /proc/mm, the host needs to be patched using\n"
"    the current skas3 patch.\n\n");

static int __init noptracefaultinfo_cmd_param(char *str, int* add)
{
	ptrace_faultinfo = 0;
	return 0;
}

__uml_setup("noptracefaultinfo", noptracefaultinfo_cmd_param,
"noptracefaultinfo\n"
"    Turns off usage of PTRACE_FAULTINFO, even if host supports\n"
"    it. To support PTRACE_FAULTINFO, the host needs to be patched\n"
"    using the current skas3 patch.\n\n");

366 367 368 369 370 371 372 373 374 375 376 377
static int __init noptraceldt_cmd_param(char *str, int* add)
{
	ptrace_ldt = 0;
	return 0;
}

__uml_setup("noptraceldt", noptraceldt_cmd_param,
"noptraceldt\n"
"    Turns off usage of PTRACE_LDT, even if host supports it.\n"
"    To support PTRACE_LDT, the host needs to be patched using\n"
"    the current skas3 patch.\n\n");

L
Linus Torvalds 已提交
378
#ifdef UML_CONFIG_MODE_SKAS
379
static inline void check_skas3_ptrace_faultinfo(void)
L
Linus Torvalds 已提交
380 381
{
	struct ptrace_faultinfo fi;
382
	void *stack;
383
	int pid, n;
L
Linus Torvalds 已提交
384

385
	non_fatal("  - PTRACE_FAULTINFO...");
386
	pid = start_ptraced_child(&stack);
L
Linus Torvalds 已提交
387 388 389

	n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi);
	if (n < 0) {
390
		ptrace_faultinfo = 0;
L
Linus Torvalds 已提交
391
		if(errno == EIO)
392
			non_fatal("not found\n");
393
		else
L
Linus Torvalds 已提交
394
			perror("not found");
395 396
	}
	else {
397
		if (!ptrace_faultinfo)
398
			non_fatal("found but disabled on command line\n");
399
		else
400
			non_fatal("found\n");
L
Linus Torvalds 已提交
401 402 403
	}

	init_registers(pid);
404
	stop_ptraced_child(pid, stack, 1, 1);
L
Linus Torvalds 已提交
405 406
}

407 408 409 410 411 412 413 414 415 416 417
static inline void check_skas3_ptrace_ldt(void)
{
#ifdef PTRACE_LDT
	void *stack;
	int pid, n;
	unsigned char ldtbuf[40];
	struct ptrace_ldt ldt_op = (struct ptrace_ldt) {
		.func = 2, /* read default ldt */
		.ptr = ldtbuf,
		.bytecount = sizeof(ldtbuf)};

418
	non_fatal("  - PTRACE_LDT...");
419 420 421 422 423
	pid = start_ptraced_child(&stack);

	n = ptrace(PTRACE_LDT, pid, 0, (unsigned long) &ldt_op);
	if (n < 0) {
		if(errno == EIO)
424
			non_fatal("not found\n");
425 426 427 428 429 430 431
		else {
			perror("not found");
		}
		ptrace_ldt = 0;
	}
	else {
		if(ptrace_ldt)
432
			non_fatal("found\n");
433
		else
434
			non_fatal("found, but use is disabled\n");
435 436 437 438 439 440 441 442 443 444 445 446 447
	}

	stop_ptraced_child(pid, stack, 1, 1);
#else
	/* PTRACE_LDT might be disabled via cmdline option.
	 * We want to override this, else we might use the stub
	 * without real need
	 */
	ptrace_ldt = 1;
#endif
}

static inline void check_skas3_proc_mm(void)
L
Linus Torvalds 已提交
448
{
449
	non_fatal("  - /proc/mm...");
450
	if (access("/proc/mm", W_OK) < 0) {
451
		proc_mm = 0;
452
		perror("not found");
453 454
	}
	else {
455
		if (!proc_mm)
456
			non_fatal("found but disabled on command line\n");
457
		else
458
			non_fatal("found\n");
L
Linus Torvalds 已提交
459
	}
460 461 462 463
}

int can_do_skas(void)
{
464
	non_fatal("Checking for the skas3 patch in the host:\n");
465 466 467 468 469 470 471

	check_skas3_proc_mm();
	check_skas3_ptrace_faultinfo();
	check_skas3_ptrace_ldt();

	if(!proc_mm || !ptrace_faultinfo || !ptrace_ldt)
		skas_needs_stub = 1;
L
Linus Torvalds 已提交
472

473
	return 1;
L
Linus Torvalds 已提交
474 475 476 477
}
#else
int can_do_skas(void)
{
478
	return 0;
L
Linus Torvalds 已提交
479 480
}
#endif
481 482 483 484

int __init parse_iomem(char *str, int *add)
{
	struct iomem_region *new;
485
	struct stat64 buf;
486
	char *file, *driver;
487
	int fd, size;
488 489 490 491 492 493 494 495 496

	driver = str;
	file = strchr(str,',');
	if(file == NULL){
		printf("parse_iomem : failed to parse iomem\n");
		goto out;
	}
	*file = '\0';
	file++;
497
	fd = open(file, O_RDWR, 0);
498 499 500 501 502
	if(fd < 0){
		os_print_error(fd, "parse_iomem - Couldn't open io file");
		goto out;
	}

503 504
	if(fstat64(fd, &buf) < 0){
		perror("parse_iomem - cannot stat_fd file");
505 506 507 508 509 510 511 512 513
		goto out_close;
	}

	new = malloc(sizeof(*new));
	if(new == NULL){
		perror("Couldn't allocate iomem_region struct");
		goto out_close;
	}

514
	size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
515 516 517 518 519 520 521 522 523 524

	*new = ((struct iomem_region) { .next		= iomem_regions,
					.driver		= driver,
					.fd		= fd,
					.size		= size,
					.phys		= 0,
					.virt		= 0 });
	iomem_regions = new;
	iomem_size += new->size + UM_KERN_PAGE_SIZE;

525
	return 0;
526
 out_close:
527
	close(fd);
528
 out:
529
	return 1;
530 531
}

532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559

/* Changed during early boot */
int pty_output_sigio = 0;
int pty_close_sigio = 0;

/* Used as a flag during SIGIO testing early in boot */
static volatile int got_sigio = 0;

static void __init handler(int sig)
{
	got_sigio = 1;
}

struct openpty_arg {
	int master;
	int slave;
	int err;
};

static void openpty_cb(void *arg)
{
	struct openpty_arg *info = arg;

	info->err = 0;
	if(openpty(&info->master, &info->slave, NULL, NULL, NULL))
		info->err = -errno;
}

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
static int async_pty(int master, int slave)
{
	int flags;

	flags = fcntl(master, F_GETFL);
	if(flags < 0)
		return -errno;

	if((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) ||
	   (fcntl(master, F_SETOWN, os_getpid()) < 0))
		return -errno;

	if((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0))
		return -errno;

	return(0);
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
static void __init check_one_sigio(void (*proc)(int, int))
{
	struct sigaction old, new;
	struct openpty_arg pty = { .master = -1, .slave = -1 };
	int master, slave, err;

	initial_thread_cb(openpty_cb, &pty);
	if(pty.err){
		printk("openpty failed, errno = %d\n", -pty.err);
		return;
	}

	master = pty.master;
	slave = pty.slave;

	if((master == -1) || (slave == -1)){
		printk("openpty failed to allocate a pty\n");
		return;
	}

	/* Not now, but complain so we now where we failed. */
	err = raw(master);
	if (err < 0)
		panic("check_sigio : __raw failed, errno = %d\n", -err);

603
	err = async_pty(master, slave);
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
	if(err < 0)
		panic("tty_fds : sigio_async failed, err = %d\n", -err);

	if(sigaction(SIGIO, NULL, &old) < 0)
		panic("check_sigio : sigaction 1 failed, errno = %d\n", errno);
	new = old;
	new.sa_handler = handler;
	if(sigaction(SIGIO, &new, NULL) < 0)
		panic("check_sigio : sigaction 2 failed, errno = %d\n", errno);

	got_sigio = 0;
	(*proc)(master, slave);

	close(master);
	close(slave);

	if(sigaction(SIGIO, &old, NULL) < 0)
		panic("check_sigio : sigaction 3 failed, errno = %d\n", errno);
}

static void tty_output(int master, int slave)
{
	int n;
	char buf[512];

	printk("Checking that host ptys support output SIGIO...");

	memset(buf, 0, sizeof(buf));

	while(os_write_file(master, buf, sizeof(buf)) > 0) ;
	if(errno != EAGAIN)
		panic("check_sigio : write failed, errno = %d\n", errno);
	while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ;

	if(got_sigio){
		printk("Yes\n");
		pty_output_sigio = 1;
	}
	else if(n == -EAGAIN) printk("No, enabling workaround\n");
	else panic("check_sigio : read failed, err = %d\n", n);
}

static void tty_close(int master, int slave)
{
	printk("Checking that host ptys support SIGIO on close...");

	close(slave);
	if(got_sigio){
		printk("Yes\n");
		pty_close_sigio = 1;
	}
	else printk("No, enabling workaround\n");
}

void __init check_sigio(void)
{
	if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) &&
	   (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){
		printk("No pseudo-terminals available - skipping pty SIGIO "
		       "check\n");
		return;
	}
	check_one_sigio(tty_output);
	check_one_sigio(tty_close);
}

void os_check_bugs(void)
{
	check_ptrace();
	check_sigio();
}