Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 7 Jul 2015 16:11:43 +1000 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Ian Lepore <ian@freebsd.org>
Cc:        Neel Natu <neel@freebsd.org>, src-committers@freebsd.org,  svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   Re: svn commit: r285217 - head/usr.sbin/bhyve
Message-ID:  <20150707122407.O1017@besplex.bde.org>
In-Reply-To: <1436213492.1334.64.camel@freebsd.org>
References:  <201507061933.t66JXTtJ050058@repo.freebsd.org> <1436213492.1334.64.camel@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, 6 Jul 2015, Ian Lepore wrote:

> On Mon, 2015-07-06 at 19:33 +0000, Neel Natu wrote:
>> Author: neel
>> Date: Mon Jul  6 19:33:29 2015
>> New Revision: 285217
>> URL: https://svnweb.freebsd.org/changeset/base/285217
>>
>> Log:
>>   Always assert DCD and DSR in bhyve's uart emulation.
>>
>>   The /etc/ttys entry for a serial console in FreeBSD/x86 is as follows:
>>   ttyu0   "/usr/libexec/getty 3wire"      vt100   onifconsole secure
>>
>>   The initial terminal type passed to getty(8) is "3wire" which sets the
>>   CLOCAL flag. However reset(1) clears this flag and any programs that try
>>   to open the terminal will hang waiting for DCD to be asserted.
>>
>>   Fix this by always asserting DCD and DSR in the emulated uart.
>>
>>   The following discussion on virtualization@ has more details:
>>   https://lists.freebsd.org/pipermail/freebsd-virtualization/2015-June/003666.html
>
> This seems like a wrong fix.  A real 3-wire serial console doesn't have
> DCD and DSR wired on.  Why isn't the right fix here having the user with
> this problem to do "stty -f /dev/ttyu0.lock clocal", maybe in rc.local?

That is the correct fix.  But not in rc.local.  rc.d/serial already
has support for this, added in 2006.  However, rc.d/serial was broken
in 2008, in part by removing support for the ioctls used by
comcontrol(8) which is used by rc.d/serial.  Setting CLOCAL in
rc.d/serial doesn't require using comcontrol, but the 3wire entry in
rc.d/serial starts with a call to the terminal() function and terminal()
uses comcontrol to reset to a default state before making minor adjustments.

There is a configuration problem for rc.d/serial.  It does nothing by
default, since it just has some useful functions and some commented-out
examples of some useful configurations.  There is no way to pass it
parameters to tell it what to do.  You have to edit it to configure it.

If you actually use rc.d/serial by uncommenting almost anything, then
the comcontrol breakage is visible as error messages from comcontrol
at boot time.  Other breakage is more subtle.  E.g., rc.d/serial
documents the system default state and resets to it, but the default
state was broken in 2008 and rc.d/serial was not broken to match.

> Hmmm, or maybe it would be right for getty to do the equivelent when it
> sees a 3wire type?

That would be another wrong thing, as is the existence of the 3wire type.
tty*.lock exists to prevent broken programs like reset(1), getty(8) and
cu(1) from breaking the settings, without hard-coding the policy or
duplicating configuration details in all programs.  It is too hard to
fix all such programs.  The 3write configuration isn't duplicated, so
it only works for getty.

The breakage in reset(1) is part of the following mostly-hard-coded settings
for c_cflag:

% 	mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
% 	mode.c_cflag |= (CS8 | CREAD);
% ...
% 	if (tgetflag("EP")) {
% 		mode.c_cflag |= PARENB;
% 		mode.c_cflag &= ~PARODD;
% 	}
% 	if (tgetflag("OP")) {
% 		mode.c_cflag |= PARENB;
% 		mode.c_cflag |= PARODD;
% 	}

This clobbers CLOCAL and many other settings.  The only ones that can be
controlled are PARENB and PARODD.  But to control them, you have to set
EP and OP in your termcap.  All the settings in /etc/gettytab are ignored,
except possibly ones for parity if getty somehow arranges to put them in a 
in termcap and nothing clobbers this.

"stty sane" is also quite broken, but not for CLOCAL.  It uses a hard-coded
default for c_cflag except for CLOCAL:

 	ip->t.c_cflag = TTYDEF_CFLAG | (ip->t.c_cflag & CLOCAL);

Most other termios flags and perhaps most control characters do need to
be reset by reset(1) and "stty sane", by the definition of resetting.
Even CLOCAL might need to be reset, but it is unclear how to control this.

TTYDEF_CFLAG is essentially getty's default c_cflag.  It is defined in
<sys/ttydefaults.h> and is a good default for the initial state (set
by drivers) too.  Other TTYDEF_*FLAG is also essentially getty's defaults.
These are also defined in the <sys/ttydefaults.h>, but are bad for the
initial state unless the device is initially (before getty) a console.
I fixed this in my drivers in 1993, but the bug is back in most drivers
now.  rc.d/serial still documents the fixed initial state (all other
flags 0).

getty(8) is not very broken.  It needs to change from the initial state
to settings suitable for logins.  <sys/ttydefaults.h> gives such settings.
The defaults need to be modified in just a few cases.  More cases in 1985
than now, since serial ttys were actually often used in 1985, but
gettytab is larger now.  It has silly old entries like:

# Fast dialup terminals, 2400/1200/300 rotary (can start either way)

300 is not too fast, but was not very slow in 1985.

... and silly new entries like:

# Entries for 3-wire serial terminals.  These don't supply carrier, so
# clocal needs to be set, and crtscts needs to be unset.

It is painful to have to edit either /etc/ttys or /etc/rc.d/serial to
configure the settings, but configuring this in rc.d/serial (or directly
in the initial state and lock state devices) has the advantage of working
for all programs, while gettytab only works for programs that understand
it (just getty(8)?).  rc.d/seral also gives more flexibility than
gettytab for the 3wire case.  The 3wire entries in gettytab are primitive
and don't use tc to extend other entries.  They enforce no parity, no
clocal, and a speed, and accept the default of crtscts being unset by
not forcing it to be set.

I didn't check the breakage in cu(1), but know that it includes
clobbering the device's default speed with a hard coded speed of 9600.
The version of cu in Taylor uucp didn't have this bug.  ISTR that
Taylor had a lot of complications for crtscts that are unnecessary if
the system default for crtscts is correct.

cu(1) of course doesn't know anything about gettytab, and uses its own
config file /etc/remote.

So to configure a serial device, the following files need to be edited:
- /etc/ttys
- termcap files or environment (I set the TERMCAP environment variable
   in ~/.profile to extend the default of cons25 for other reasons)
- /etc/gettytab (if you need a non-default entry)
- /etc/remote (if you need a non-default entry.  Devices can also be
   selected in it)
- /etc/rc.d/serial (devices must be selected in it.  Also change it if
   you need a non-default entry)
It is usually easier not to change all of these.  Just use /etc/rc.d/serial
or direct sttys on the control devices to fixate a couple of important
settings, and depend on defaults for the rest.  This is a hack.  It
depends on normal programs not knowing anything about the lock devices,
and on having either correct or suitably broken handling of errors
from tcsetattr().
   (tcsetattr() is remarkably hard to use.  It is supposed to return
   success iff it set at least 1 attribute successfully, but it can
   almost always set at least 1 attribute correctly, e.g., by setting
   an unimportant control character to its previous value.  So to
   determine which attributes were set as requested, the callers should
   use tcgetattr() after tcsetattr() and check for differences.  Changes
   to individual attributes will fail quite often because the hardware
   doesn't support the change or the system denies it using the lock
   state device or otherwise, so callers should be prepared for many
   differences and only complain about important ones.  I haven't seen
   any programs that do much more than check the return value of
   tcsetattr().  It would be hard to decide if a large difference like
   denying a speed or parity change is important.)

Some time after 1985, files with default configurations like
/etc/gettytab and /etc/remote proved to be far too simple.  E.g.,
communicating with an intelligent modem usually requires a complicated
set of commands that is very device-dependent.  It is simpler to have
this in a script for just one device at a time.

Other known bugs involving CLOCAL:
- uart used to "fixate" CLOCAL to on for serial consoles, without even
   making this visible in the initial state or lock state devices, and
   without allowing this to be controlled by the initial state or
   lock devices.  This gave correct operation, except it didn't allow
   the controlling CLOCAL in the usual way using the control devices.
   (Unsetting CLOCAL for a console would usually be foot-shooting, but
   the sysadmin might know better.)  Now, uart doesn't do anything
   special for CLOCAL, so the other bugs break it.
- tty_init_console() doesn't set CLOCAL in the lock state device.  So
   without fixation, bugs like the one in reset(1) break serial consoles.
- many console drivers don't even call tty_init_console().  This gives
   them backwards settings for CLOCAL in the initial state device too.
   It asks for wronger settings than that, but the default settings are
   broken by using getty's defaults for non-consoles.
- ttydev_open() doesn't honor CLOCAL for callout devices.  It ignores
   the initial state device's setting of CLOCAL and forces CLOCAL on.
   This usually just breaks later operation of carrier.  However, it
   doesn't even do that if a buggy program like reset(1) accidentally
   changes CLOCAL back to its correct value.

Other known bugs near CLOCAL:
- tty_init_console() doesn't clear HUPCL in the initial state device.
   This is a very large bug.  Most of the tty bugs described here are
   from 2008, but this one is from 2004.
- tty_init_console() doesn't set HUPCL in the lock state device
- uart still fixates HUPCL for serial consoles.  That means ignoring it
   and acting as if it is off.  This works around the 2004 bug above.
   Before 2004, uart didn't support the initial/lock state devices, so
   it needed both of the fixation hacks.  After 2004, it still needed
   this one.  After 2008, it needed the one for CLOCAL.
- sio still fixates HUPCL for serial consoles (in my unbroken version
   for -current, though not in my development version).  Before 2004,
   it had the only example of correct setting of HUPCL for consoles.
   But it also fixated HUPCL for consoles, using even uglier code with
   the same result.  Thus it wasn't broken by the 2004 bug, but was
   always broken by fixation.
- uart doesn't honor HUPCL.  Combined with fixation, this gives hangup
   on close never for consoles and always otherwise.  I.e., HUPCL is
   gnored and the condition of not being a console is used to replace
   the correct condition of HUPCL being set.
- tty_init_console() doesn't lock speeds in the lock state device.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20150707122407.O1017>