lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <CAOmKuSpFcy8g9yPasWngtHqpurJrqLeyB5ppt33TodpsU_DzqQ@mail.gmail.com>
Date:	Fri, 27 Sep 2013 15:41:58 +0300
From:	Alexey Pelykh <alexey.pelykh@...il.com>
To:	Russell King - ARM Linux <linux@....linux.org.uk>
Cc:	Greg KH <gregkh@...uxfoundation.org>,
	Tony Lindgren <tony@...mide.com>,
	Linux Kernel Mailing List <linux-kernel@...r.kernel.org>,
	Felipe Balbi <balbi@...com>,
	"linux-serial@...r.kernel.org" <linux-serial@...r.kernel.org>,
	Linux OMAP Mailing List <linux-omap@...r.kernel.org>,
	"linux-arm-kernel@...ts.infradead.org" 
	<linux-arm-kernel@...ts.infradead.org>
Subject: Re: [PATCH] OMAP/serial: Fix division by zero exception on 3M+ baud rates

Sorry for HTML mail, resend

On Fri, Sep 27, 2013 at 3:37 PM, Alexey Pelykh <alexey.pelykh@...il.com> wrote:
> Hi Russel,
>
> Probably then it would be great if you could summarize that in a patch to
> replace mine, since basically mine is totally improper.
>
> Thanks,
> Alexey
>
>
>
> On Sat, Sep 21, 2013 at 1:42 PM, Russell King - ARM Linux
> <linux@....linux.org.uk> wrote:
>>
>> On Sat, Sep 21, 2013 at 03:43:44AM -0400, Alexey Pelykh wrote:
>> > diff --git a/drivers/tty/serial/omap-serial.c
>> > b/drivers/tty/serial/omap-serial.c
>> > index 816d1a2..808a880 100644
>> > --- a/drivers/tty/serial/omap-serial.c
>> > +++ b/drivers/tty/serial/omap-serial.c
>> > @@ -240,8 +240,8 @@ serial_omap_baud_is_mode16(struct uart_port *port,
>> > unsigned int baud)
>> >  {
>> >       unsigned int n13 = port->uartclk / (13 * baud);
>> >       unsigned int n16 = port->uartclk / (16 * baud);
>> > -     int baudAbsDiff13 = baud - (port->uartclk / (13 * n13));
>> > -     int baudAbsDiff16 = baud - (port->uartclk / (16 * n16));
>> > +     int baudAbsDiff13 = n13 ? (baud - (port->uartclk / (13 * n13))) :
>> > INT_MAX;
>> > +     int baudAbsDiff16 = n16 ? (baud - (port->uartclk / (16 * n16))) :
>> > INT_MAX;
>> >       if(baudAbsDiff13 < 0)
>> >               baudAbsDiff13 = -baudAbsDiff13;
>> >       if(baudAbsDiff16 < 0)
>>
>> So, this code is trying to work out whether it's better to use a prescaler
>> of 13 or 16?  In which case, the above code is rather buggy in many ways.
>> Let's take an example - what if port->uartclk is 19MHz?
>>
>> n13 = 19M / 13 * 115200 = 1
>> n16 = 19M / 16 * 115200 = 1
>> baudAbsDiff13 = 115200 - (19M / 13 * 1) = 115200 - 146153 = -30953 ->
>> 30953
>> baudAbsDiff16 = 115200 - (19M / 16 * 1) = 115200 - 118750 = -3550 -> 3350
>>
>> return (baudAbsDiff13 > baudAbsDiff16); -> 1
>>
>> That seems like it's right.
>>
>> Now, what if it's 18MHz?
>>
>> n13 = 18M / 13 * 115200 = 1
>> n16 = 18M / 16 * 115200 = 0
>> baudAbsDiff13 = 115200 - (18M / 13 * 1) = 115200 - 146153 = -23261 ->
>> 23261
>> baudAbsDiff16 = 115200 - (18M / 16 * 0) = 115200 - 118750 = INT_MAX
>>
>> return (baudAbsDiff13 > baudAbsDiff16); -> 0
>>
>> Now, consider the question: with a 18MHz clock, which produces a more
>> accurate 115200 baud rate - a prescaler of 16 or 13?  Let's go back to
>> the math.
>>
>>         115200 * 13 => 1497600
>>         115200 * 16 => 1843200
>>
>> So, choosing a prescaler of 16 will give a slower baud rate, but it's
>> a lot closer to 115200 than using the prescaler of 13.  Yet, the code
>> will select the latter.
>>
>> I'd suggest that this code gets rewritten to do "what it says on the tin"
>> a bit better:
>>
>>         unsigned n13 = DIV_ROUND_CLOSEST(port->uartclk, 13 * baud);
>>         unsigned n16 = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
>>         int delta_clk_13 = 13 * baud * n13 - port->uartclk;
>>         int delta_clk_16 = 16 * baud * n16 - port->uartclk;
>>
>>         if (delta_clk_13 < 0)
>>                 delta_clk_13 = -delta_clk_13;
>>         if (delta_clk_16 < 0)
>>                 delta_clk_16 = -delta_clk_16;
>>
>>         return delta_clk_13 > delta_clk_16;
>>
>> Note that baud will never be larger than port->uartclk / 13, so n13
>> will always be greater than 1.  n16 may be zero though, and in this
>> case, at the point of the test, delta_clk_16 becomes much larger than
>> delta_clk_13, so the above calculation returns false, meaning we
>> correctly use a prescaler of 13.
>>
>> Not only does this avoid the problem with the divider being zero, but
>> it also selects the prescaler which gives us the closest baud rate to
>> the one which is being requested.
>>
>> Finally, serial_omap_get_divisor should also use DIV_ROUND_CLOSEST() -
>> or for extra points, integrate this into serial_omap_get_divisor(),
>> and have it also return the prescaler too.
>>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ