Just a few jots on handling packages in Ubuntu. This post is a true mess.
Pinning
The bottom line seems to be not to use the Software Updater, but instead go
# apt-get upgrade
How to prevent certain packages from being updated, based upon this Pinning Howto page and the Apt Preferences page which cover the internals as well.
There also the manpage:
$ man apt_preferences
Repositories
The repositories known by apt-get are listed in /etc/apt/sources.list.d/ and /etc/apt/sources.list. For example, adding a repository:
# add-apt-repository ppa:vovoid/vsxu-release
Removing a repository e.g.
# add-apt-repository --remove ppa:vovoid/vsxu-release
and always do
# apt-get update
after changing the repository set. Now, you might get something like
E: The repository 'http://ppa.launchpad.net/...' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default
If you want to insist on using such repository (at your own risk), go
# apt update --allow-insecure-repositories
and may the force be with you.
Among others, adding the repository above creates /etc/apt/sources.list.d/vovoid-vsxu-release-trusty.list saying
deb http://ppa.launchpad.net/vovoid/vsxu-release/ubuntu trusty main
# deb-src http://ppa.launchpad.net/vovoid/vsxu-release/ubuntu trusty main
“trusty” refers to Ubuntu 14.04, of course.
Look at this page for more info.
Checking what apt-get would install
# apt-get -s upgrade | less
The packages related to the Linux kernel: linux-generic linux-headers-generic linux-image-generic
It’s worth looking here on this regrading what packages “kept back means” (but the bottom line is that these packages won’t be installed).
Being open to suggestions
Kodi, for example, has a lot of “side packages” that are good to install along. This is how to tell apt-get to grab them as well:
# apt-get install --install-suggests kodi
Pinning with dpkg
This doesn’t work with apt-get nor Automatic Updater, following this and this web pages:
List all packages
$ dpkg -l
Wildcards can be used to find specific packages. For example, those related to the current kernel:
$ dpkg -l "*$(uname -r)*"
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-=============================================-===========================-===========================-===============================================================================================
ii linux-headers-3.13.0-35-generic 3.13.0-35.62 amd64 Linux kernel headers for version 3.13.0 on 64 bit x86 SMP
ii linux-image-3.13.0-35-generic 3.13.0-35.62 amd64 Linux kernel image for version 3.13.0 on 64 bit x86 SMP
ii linux-image-extra-3.13.0-35-generic 3.13.0-35.62 amd64 Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
Or, to get just the package names:
$ dpkg -l | awk '{ print $2; }' | grep "$(uname -r)"
Pinning a package
Aug 2019 update: Maybe with apt-mark? Haven’t tried that yet.
In order to prevent a certain package from being updated, use the “hold” setting for the package. For example, holding the kernel related package automatically (all three packages) as root:
# dpkg -l | awk '{ print $2; }' | grep "$(uname -r)" | while read i ; do echo $i hold ; done | dpkg --set-selections
After this, the listing of these packages is:
$ dpkg -l "*$(uname -r)*"
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-=============================================-===========================-===========================-===============================================================================================
hi linux-headers-3.13.0-35-generic 3.13.0-35.62 amd64 Linux kernel headers for version 3.13.0 on 64 bit x86 SMP
hi linux-image-3.13.0-35-generic 3.13.0-35.62 amd64 Linux kernel image for version 3.13.0 on 64 bit x86 SMP
hi linux-image-extra-3.13.0-35-generic 3.13.0-35.62 amd64 Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
Indeed, the “h” notes that the packages are held. To revert this, use “install” instead of “hold” in the input to dpkg –set-selections above.
Which package provides file X?
Following this page, install apt-file (simply with apt-get install apt-file), go “apt-file update” once and then go something like (not necessarily as root):
$ apt-file find libgnome-2.so
Note that the pattern can be a substring (as in the example above).
What files does package X generate?
$ dpkg -L libpulse-dev
Installing a deb file locally
# dpkg -i thepackage.deb
If there are failed dependencies, fix them with apt-get subsequently:
# apt-get -f install
and if it says that it wants to remove the package you tried to install, go
# apt-get install -f --fix-missing
That will probably not help directly, but odds are apt-get will at least explain why it wants to kick out the package.
To make apt ignore a failed post-installation script, consider this post.
Extracting the files from a repository
This can be used for running more than one version of Google Chrome on a computer. See this post for a few more words on this.
Extract the .deb package:
$ ar x google-chrome-stable_current_amd64.deb
Note that the files go into the current directory (yuck).
Extract the package’s files:
$ mkdir files
$ cd files
$ tar -xJf ../data.tar.xz
Extract the installation scripts:
$ mkdir scripts
$ cd scripts/
$ tar -xJf ../control.tar.xz
A word on repositories
Say that we have a line like this in /etc/apt/sources.list:
deb http://archive.ubuntu.com/ubuntu xenial main universe updates restricted security backports
It tells apt-update to go to http://archive.ubuntu.com/ubuntu/dists/ and look into xenial/main for the “main” part, xenial/universe for the “universe” part but e.g. xenial-updates/ for the “updates”. This site help with a better understanding on how a sources.list file is set up.
If we look at e.g. ubuntu/dists/xenial/main/, there’s a binary-amd64/ subdirectory for the amd64 platforms (64-bit Intel/AMD). That’s where the Packages.gz and Packages.xz files are found. These list the packages available in the repositories, but even more important: where to find them.
For example, the entry for the “adduser” package looks like this:
Package: adduser
Priority: required
Section: admin
Installed-Size: 648
Maintainer: Ubuntu Core Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Debian Adduser Developers <adduser-devel@lists.alioth.debian.org>
Architecture: all
Version: 3.113+nmu3ubuntu4
Replaces: manpages-it (<< 0.3.4-2), manpages-pl (<= 20051117-1)
Depends: perl-base (>= 5.6.0), passwd (>= 1:4.1.5.1-1.1ubuntu6), debconf | debconf-2.0
Suggests: liblocale-gettext-perl, perl-modules, ecryptfs-utils (>= 67-1)
Filename: pool/main/a/adduser/adduser_3.113+nmu3ubuntu4_all.deb
Size: 161698
MD5sum: 36f79d952ced9bde3359b63cf9cf44fb
SHA1: 6a5b8f58e33d5c9a25f79c6da80a64bf104e6268
SHA256: ca6c86cb229082cc22874ed320eac8d128cc91f086fe5687946e7d05758516a3
Description: add and remove users and groups
Multi-Arch: foreign
Homepage: http://alioth.debian.org/projects/adduser/
Description-md5: 7965b5cd83972a254552a570bcd32c93
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Origin: Ubuntu
Supported: 5y
Task: minimal
As is evident, this entry contains dependency information, but most important: It points at where the package can be downloaded from: pool/main/a/adduser/adduser_3.113+nmu3ubuntu4_all.deb in this case, which actually points to http://archive.ubuntu.com/ubuntu/pool/main/a/adduser/adduser_3.113+nmu3ubuntu4_all.deb.
Note that the URL’s base is repository’s root, and not necessarily the root of the domain. Since the Package file contains the SHA1 sum of the .deb file, its own SHA1 sum is listed in http://archive.ubuntu.com/ubuntu/dists/xenial/InRelease, which also contains a PGP signature.
The various “Contents” files (e.g. dists/xenial/Contents-amd64.gz) seem to contain a list of files, and the packages they belong to. Probably for use of apt-file.
These are a few jots about constraining in Vivado. With no particular common topic, and in no particular order. Note that I have another post on similar topics.
Setting the default IOSTANDARD for all ports
In a design where all ports have the same IOSTANDARD, it’s daunting to set them for all. So if there’s just one exception, one can go
set_property IOSTANDARD LVCMOS33 [get_ports -filter { LOC =~ IOB_* } ]
set_property IOSTANDARD LVDS_25 [get_ports clk_100_p]
It’s important to do this after placement constraints of the ports, because the LOC property is set only in conjunction with setting package_pin. Filtering based upon LOC is required to avoid inclusion of MGT ports in the get_ports command, and in turn fail the set_property command altogether (yielding not only a critical warning, but none of the IOSTANDARDs will be set).
Tell the truth about your heat sink
Vivado makes an estimation of the junction temperature, based upon its power estimation. That’s the figure that you want to keep below 85°C (if you’re using a commercial temperature version of the FPGA).
With all my reservations on the accuracy of the power estimation, and hence the temperature it calculates based upon in, it makes sense to tell Vivado about the chosen cooling solution. Otherwise, it assumes a heatsink with a healthy fan above it. So if you like to live on the edge, like me, and work without a noisy fan, these two lines in the XDC file tell Vivado to adjust the effective Junction-to-Air thermal resistance (Θ_JA).
set_operating_conditions -airflow 0
set_operating_conditions -heatsink low
It’s also possible to set Θ_JA explicitly with -thetaja. Try “help set_operating_conditions” at the Tcl prompt for a list of options.
Frankly speaking, the predicted junction temperature stated on the power report is probably rubbish anyhow, even if the power estimation is accurate. The reason is that there’s a low thermal resistance towards the board: If the board remains on 25°C, the junction temperature will be lower than predicted. On the other hand, if the board heats up from adjacent components, a higher temperature will be measured. In a way, the FPGA will serve as a cooling path from the board to air. With extra power flowing through this path, the temperature rises all along it.
For example, the temperature I got with the setting above on a KC705, with the fan taken off, was significantly higher (~60°C) than Vivado’s prediction (44°C) on a design that had little uncertainty (90% of the estimated power was covered by static power and GTXs with a fixed rate — there was almost no logic in the design). The junction temperature was measured through JTAG from the Hardware Manager.
So the only thing that really counts is a junction temperature after 15 minutes or so.
Search patterns for finding elements
Pattern-matching in Vivado is slash-sensitive. e.g.
foreach x [get_pins "*/*/a*"] { puts $x }
prints elements pins three hierarchies down beginning with “a”, but “a*” matches only pins on the top level.
The “foreach” is given here to demonstrate loops. It’s actually easier to go
join [get_pins "*/*/a*"] "\n"
To make “*” match any character “/” included, it’s possible, yet not such a good idea, to use UCF-style, e.g.
foreach x [get_pins -match_style ucf "*/fifo*"] { puts $x }
or a more relevant example
get_pins -match_style ucf */PS7_i/FCLKCLK[1]
The better way is to forget about the old UCF file format. The Vivado way to allow “*” to match any character, including a slash, is filters:
set_property LOC GTXE2_CHANNEL_X0Y8 [get_cells -hier -filter {name=~*/gt0_mygtx_i/gtxe2_i}]
Another important concept in Vivado is the “-of” flag which allows to find all nets connected to a cell, all cells connected to a net etc.
For example,
get_nets -of [get_cells -hier -filter {name=~*/gt_top_i/phy_rdy_n_int_reg}]
Group clocks instead of a lot of false paths
Unlike ISE, Vivado assumes that all clocks are “related” — if two clocks come from sources, which the tools have no reason to assume a common source for, ISE will consider all paths between the clock domains as false paths. Vivado, on the other hand, will assume that these paths are real, and probably end up with an extreme constraint, take ages in the attempt to meet timing, and then fail the timing, of course.
Even in reference designs, this is handled by issuing false paths between each pair of unrelated clocks (two false path statements for each pair, usually). This is messy, often with complicated expressions appearing twice. And a lot of issues every time a new clock is introduced.
The clean way is to group the clocks. Each group contains all clocks that are considered related. Paths inside a group are constrained. Paths between groups are false. Simple and intuitive.
set_clock_groups -asynchronous \
-group [list \
[get_clocks -include_generated_clocks -of_objects [get_pins -hier -filter {name=~*gt0_mygtx_i*gtxe2_i*TXOUTCLK}]] \
[get_clocks -include_generated_clocks "gt0_txusrclk_i"]] \
-group [get_clocks -include_generated_clocks "drpclk_in_i"] \
-group [list \
[get_clocks -include_generated_clocks "sys_clk"] \
[get_clocks -include_generated_clocks -of_objects [get_pins -hier -filter {name=~*/pipe_clock/pipe_clock/mmcm_i/*}]]]
In the example above, three clock groups are declared.
As a group often consists of several clocks, each requiring a tangled expression to pin down, it may come handy to define a list of clocks, with the “list” TCL statement, as shown above.
Another thing to note is that clocks can be obtained as all clocks connected to a certain MMCM or PLL, as shown above, with -of_objects. To keep the definitions short, it’s possible to use create_generated_clock to name clocks that can be found in certain parts of the design (create_clock is applied to external pins only).
If a clock is accidentally not included in this statement, don’t worry: Vivado will assume valid paths for all clock domain crossings involving it, and it will probably take a place of honor in the timing report.
Finally, it’s often desired to tell Vivado to consider clocks that are created by an MCMM / PLL as independent. If a Clock Wizard IP was used, it boils down to something as simple as this:
set_clock_groups -asynchronous \
-group [get_clocks -include_generated_clocks -of_objects [get_pins -hier -filter {name=~*clk_gen_ins/clk_in1}]] \
-group [get_clocks -include_generated_clocks -of_objects [get_pins -hier -filter {name=~*clk_gen_ins/clk_out1}]]
which simply says “the input and output clocks of the clock module are independent”. This can be expanded to more outputs, of course.
Telling the tools what the BUFGCE/BUFGMUX is set to
Suppose a segment like this:
BUFGCE clkout1_buf
(.O (slow_clk),
.CE (seq_reg1[7]),
.I (clkout1));
To tell the tools that the timing analysis should be made with the assumption that BUFGCE is enabled,
set_case_analysis 1 [get_pins -hier -filter {name=~*/clkout1_buf/CE0}]
set_case_analysis 1 [get_pins -hier -filter {name=~*/clkout1_buf/S0}]
The truth is that it’s redundant in this case, as the tools assume that CE=1. But this is the syntax anyhow.
Constant clock? Who? Where? Why?
One of the things to verify before being happy with a design’s timing (a.k.a. “signing off timing”), according to UG906 (Design Analysis and Closure Techniques) is that there are no constant clocks nor unconstrained internal endpoints. But hey, what if there are? Like, when running “Report Timing Summary”, under “Check Timing” the number for “constant clock” is far from zero. And the timing summary says this:
2. checking constant clock
--------------------------
There are 2574 register/latch pins with constant_clock. (MEDIUM)
3. checking pulse_width_clock
-----------------------------
There are 0 register/latch pins which need pulse_width check
4. checking unconstrained_internal_endpoints
--------------------------------------------
There are 0 pins that are not constrained for maximum delay.
There are 5824 pins that are not constrained for maximum delay due to constant clock. (MEDIUM)
Ehm. So which clock caused this, and what are the endpoints involved? It’s actually simple to get that information. Just go
check_timing -verbose -file my_timing_report.txt
on the Tcl prompt, and read the file. The registers and endpoints are listed in the output file.
Floorplanning (placement constraints for logic)
The name of the game is Pblocks. Didn’t dive much into the semantics, but used the GUI’s Tools > Floorplanning menus to create a Pblock and auto-place it. Then saved the constraints, and manipulated the Tcl commands manually (i.e. the get_cells command and the choice of slices).
create_pblock pblock_registers_ins
add_cells_to_pblock [get_pblocks pblock_registers_ins] [get_cells -quiet -hierarchical -filter { NAME =~ "registers_ins/*" && PRIMITIVE_TYPE =~ FLOP_LATCH.*.* && NAME !~ "registers_ins/fifo_*" }]
resize_pblock [get_pblocks pblock_registers_ins] -add {SLICE_X0Y0:SLICE_X151Y99}
The snippet above places all flip-flops (that is, registers) of a certain module, except for those belonging to a couple of submodules (excluded by the second NAME filter) to the bottom area of a 7V330T. The constraint is non-exclusive (other logic is allowed in the region as well).
The desired slice region was found by hovering with the mouse over a zoomed in chip view of an implemented design.
The tools obeyed this constraint strictly, even with post-route optimization, so it’s important not to shoot yourself in the foot when using this for timing improvement (in my case it worked).
To see how the logic is spread out, use the “highlight leaf cells” option when right-clicking a hierarchy in the netlist view to the left of a chip view. Or even better, use Tcl commands on the console:
unhighlight_objects
highlight_objects -color red [get_cells -hierarchical -filter { NAME =~ "registers_ins/*" && PRIMITIVE_TYPE =~ FLOP_LATCH.*.* && NAME !~ "registers_ins/fifo_*" }]
The first command removes existing highlight. There’s an -rgb flag too for selecting the exact color.
There’s also show_objects -name mysearch [ get_cells ... ] which is how the GUI’s “find” operation creates those lists in GUI to inspect elements.
When a Windows 7 or Windows 8 starts to behave weirdly, this is the general-purpose command that can save your day (in the Command Prompt):
sfc /scannow
It scans all system files and fixes whatever looks bad. In my case, it started off as a “Limited” Wireless connection on a laptop (after it had been fine for a year), which turned out to be the lack of a DHCP request, and ended up with the understanding the the DHCP request service can’t be started because some “element” was missing. Now go fix that manually.
The scan took some 30 minutes, but after the reboot, all was fine again.
For more of my war stories, click here.
Introduction
Needing to remove superfluous memory barriers from a Linux kernel device driver, I wondered what they actually do. The issue is discussed down to painful detail in Documentation/memory-barriers.txt, but somehow it’s quite difficult to figure out if they’re really needed and where. Most drivers rely on subsequent iowrite32′s (or writel’s) to arrive to the hardware in the same order they appear in the code, and this is backed up the following clause in memory-barriers.txt:
Inside of the Linux kernel, I/O should be done through the appropriate accessor routines – such as inb() or writel() – which know how to make such accesses appropriately sequential. Whilst this, for the most part, renders the explicit use of memory barriers unnecessary, there are a couple of situations where they might be needed:
- On some systems, I/O stores are not strongly ordered across all CPUs, and so for _all_ general drivers locks should be used and miowb() must be issued prior to unlocking the critical section.
- If the accessor functions are used to refer to an I/O memory window with relaxed memory access properties, then _mandatory_ memory barriers are required to enforce ordering.
See Documentation/DocBook/deviceiobook.tmpl for more information.
So what they’re saying is that a memory barrier should be used before releasing a lock (spinlock? mutex? both? The examples show only a spinlock) and when prefetching is allowed by hardware.
Nice. Are they doing anything?
April 2020 update: I’ve written a new post on a similar topic. Also, on top of memory-barriers.txt mentioned above, there are some excellent explanations in the kernel tree’s tools/memory-model/Documentation/explanation.txt and tools/memory-model/Documentation/recipes.txt. There are relatively new (from v4.17, beginning of 2018).
May 2021 update: I’ve also written the parallel post for Windows device driver coding, which occasionally brings up Linux.
The practical take
Since I care most about x86 and ARM, I decided to figure out what the memory barriers actually do. The driver’s code should be formally correct, but in the end, if I remove a memory barrier and then test the driver — have I really made a difference? Have I really tested anything?
Ah, and in case you wonder why I didn’t check ioread32() and readl(): I don’t use them in my driver. Odd as it may sound.
The kernel sources in this post are ~3.12 but how often does anyone dare touching those basic functions?
Spoiler
For the lazy ones, here are my conclusions:
- On x86 platforms, iowrite32() and writel() are translated to just a “mov” into memory.
- On ARM, the same functions translate into a full write synchronization barrier (stop execution until all previous writes are done), and then an “str” into memory.
- On x86, the following functions translate into nothing: mmiowb(), smp_wmb() and smp_rmb(). wmb() and rmb() translate into “sfence” and “lfence” respectively.
- On ARM, mmiowb() translates into nothing. The other barriers translate into sensible opcodes.
Trying memory barriers with iowrite32()
I wrote the following kernel module as minimodule.c. Obviously, it won’t do anything good except for being disassembled after compilation.
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
void try_iowrite32(void) {
void __iomem *p = (void *) 0x12345678;
iowrite32(0xabcd0001, p);
iowrite32(0xabcd0001, p);
iowrite32(0xabcd0002, p);
mmiowb();
iowrite32(0xabcd0003, p);
wmb();
iowrite32(0xabcd0004, p);
rmb();
iowrite32(0xabcd0005, p);
smp_wmb();
iowrite32(0xabcd0006, p);
smp_rmb();
}
EXPORT_SYMBOL(try_iowrite32);
The idea: First repeat exactly the same write to see how that’s handled, and then add barriers to see what they turn into.
The related sources for iowrite32() on x86
I have to admit that I was surprised to find out that iowrite32() is a function in itself, as is shown later in the disassembly. My best understanding was that it’s just an alias for writel(), by virtue of a define statement. But since CONFIG_GENERIC_IOMAP is defined on my kernel, it’s not defined in include/asm-generic/io.h, but there’s just a header for it in include/asm-generic/iomap.h. It’s defined as a function in lib/iomap.c as follows:
void iowrite32(u32 val, void __iomem *addr)
{
IO_COND(addr, outl(val,port), writel(val, addr));
}
where IO_COND is previously defined in the same file as follows (the comment is in the sources):
/*
* Ugly macros are a way of life.
*/
#define IO_COND(addr, is_pio, is_mmio) do { \
unsigned long port = (unsigned long __force)addr; \
if (port >= PIO_RESERVED) { \
is_mmio; \
} else if (port > PIO_OFFSET) { \
port &= PIO_MASK; \
is_pio; \
} else \
bad_io_access(port, #is_pio ); \
} while (0)
So there we have it. iowrite32() isn’t just an alias for writel(), but it checks the address and interprets it as port I/O if that makes sense.
To be sure, iowrite32() was disassembled as follows from the kernel’s object code (32-bit version):
0020f79f <iowrite32>:
20f79f: 81 fa ff ff 03 00 cmp $0x3ffff,%edx
20f7a5: 89 d1 mov %edx,%ecx
20f7a7: 76 03 jbe 20f7ac <iowrite32+0xd>
20f7a9: 89 02 mov %eax,(%edx)
20f7ab: c3 ret
20f7ac: 81 fa 00 00 01 00 cmp $0x10000,%edx
20f7b2: 76 08 jbe 20f7bc <iowrite32+0x1d>
20f7b4: 81 e2 ff ff 00 00 and $0xffff,%edx
20f7ba: ef out %eax,(%dx)
20f7bb: c3 ret
20f7bc: ba f2 56 03 00 mov $0x356f2,%edx
20f7c1: 89 c8 mov %ecx,%eax
20f7c3: e9 41 fe ff ff jmp 20f609 <bad_io_access>
Results on x86_64
Compiled on Intel x86/64 bit:
$ objdump -d minimodule.ko
minimodule.ko: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <try_iowrite32>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: e8 00 00 00 00 callq 9 <try_iowrite32+0x9>
9: be 78 56 34 12 mov $0x12345678,%esi
e: bf 01 00 cd ab mov $0xabcd0001,%edi
13: e8 00 00 00 00 callq 18 <try_iowrite32+0x18>
18: be 78 56 34 12 mov $0x12345678,%esi
1d: bf 01 00 cd ab mov $0xabcd0001,%edi
22: e8 00 00 00 00 callq 27 <try_iowrite32+0x27>
27: be 78 56 34 12 mov $0x12345678,%esi
2c: bf 02 00 cd ab mov $0xabcd0002,%edi
31: e8 00 00 00 00 callq 36 <try_iowrite32+0x36>
36: be 78 56 34 12 mov $0x12345678,%esi
3b: bf 03 00 cd ab mov $0xabcd0003,%edi
40: e8 00 00 00 00 callq 45 <try_iowrite32+0x45>
45: 0f ae f8 sfence
48: be 78 56 34 12 mov $0x12345678,%esi
4d: bf 04 00 cd ab mov $0xabcd0004,%edi
52: e8 00 00 00 00 callq 57 <try_iowrite32+0x57>
57: 0f ae e8 lfence
5a: be 78 56 34 12 mov $0x12345678,%esi
5f: bf 05 00 cd ab mov $0xabcd0005,%edi
64: e8 00 00 00 00 callq 69 <try_iowrite32+0x69>
69: be 78 56 34 12 mov $0x12345678,%esi
6e: bf 06 00 cd ab mov $0xabcd0006,%edi
73: e8 00 00 00 00 callq 78 <try_iowrite32+0x78>
78: c9 leaveq
79: c3 retq
...
Those “callq” statements are modified upon linking. To resolve what these are calling, go
$ readelf -r minimodule.ko
Relocation section '.rela.text' at offset 0xa9b0 contains 8 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000005 002300000002 R_X86_64_PC32 0000000000000000 mcount - 4
000000000014 002000000002 R_X86_64_PC32 0000000000000000 iowrite32 - 4
000000000023 002000000002 R_X86_64_PC32 0000000000000000 iowrite32 - 4
000000000032 002000000002 R_X86_64_PC32 0000000000000000 iowrite32 - 4
000000000041 002000000002 R_X86_64_PC32 0000000000000000 iowrite32 - 4
000000000053 002000000002 R_X86_64_PC32 0000000000000000 iowrite32 - 4
000000000065 002000000002 R_X86_64_PC32 0000000000000000 iowrite32 - 4
000000000074 002000000002 R_X86_64_PC32 0000000000000000 iowrite32 - 4
(the output continues with relocation information for debug variables).
It’s quite easy to work this out: The “Offset” column tells us the offset in the object code. For example, a callq statement begins at 0x13, but the address to call starts at 0x14. The second entry in the relocation section points at offset 0x14, and says that the target is iowrite32().
So from this output we learn that all callq’s are to iowrite32(), except the first one, which goes to mcount() (which is intended for kernel call tracing).
Now to conclusions: There are no memory barriers in the code, except those generated by wmb() and rmb(), which added sfence and lfence respectively. sfence is defined as
Performs a serializing operation on all store instructions that were issued prior the SFENCE instruction. This serializing operation guarantees that every store instruction that precedes in program order the SFENCE instruction is globally visible before any store instruction that follows the SFENCE instruction is globally visible. The SFENCE instruction is ordered with respect store instructions, other SFENCE instructions, any MFENCE instructions, and any serializing instructions (such as the CPUID instruction). It is not ordered with respect to load instructions or the LFENCE instruction.
and lfence as
Performs a serializing operation on all load-from-memory instructions that were issued prior the LFENCE instruction. This serializing operation guarantees that every load instruction that precedes in program order the LFENCE instruction is globally visible before any load instruction that follows the LFENCE instruction is globally visible. The LFENCE instruction is ordered with respect to load instructions, other LFENCE instructions, any MFENCE instructions, and any serializing instructions (such as the CPUID instruction). It is not ordered with respect to store instructions or the SFENCE instruction.
One can feel the Intel-headache just reading this.
Results on x86 (32 bit)
Compiling this against a 32-bit kernel, with a slightly different configuration:
$ objdump -d minimodule.ko
minimodule.ko: file format elf32-i386
Disassembly of section .text:
00000000 <try_iowrite32>:
0: ba 78 56 34 12 mov $0x12345678,%edx
5: b8 01 00 cd ab mov $0xabcd0001,%eax
a: e8 fc ff ff ff call b <try_iowrite32+0xb>
f: ba 78 56 34 12 mov $0x12345678,%edx
14: b8 01 00 cd ab mov $0xabcd0001,%eax
19: e8 fc ff ff ff call 1a <try_iowrite32+0x1a>
1e: ba 78 56 34 12 mov $0x12345678,%edx
23: b8 02 00 cd ab mov $0xabcd0002,%eax
28: e8 fc ff ff ff call 29 <try_iowrite32+0x29>
2d: ba 78 56 34 12 mov $0x12345678,%edx
32: b8 03 00 cd ab mov $0xabcd0003,%eax
37: e8 fc ff ff ff call 38 <try_iowrite32+0x38>
3c: f0 83 04 24 00 lock addl $0x0,(%esp)
41: ba 78 56 34 12 mov $0x12345678,%edx
46: b8 04 00 cd ab mov $0xabcd0004,%eax
4b: e8 fc ff ff ff call 4c <try_iowrite32+0x4c>
50: f0 83 04 24 00 lock addl $0x0,(%esp)
55: ba 78 56 34 12 mov $0x12345678,%edx
5a: b8 05 00 cd ab mov $0xabcd0005,%eax
5f: e8 fc ff ff ff call 60 <try_iowrite32+0x60>
64: ba 78 56 34 12 mov $0x12345678,%edx
69: b8 06 00 cd ab mov $0xabcd0006,%eax
6e: e8 fc ff ff ff call 6f <try_iowrite32+0x6f>
73: f0 83 04 24 00 lock addl $0x0,(%esp)
78: c3 ret
79: 00 00 add %al,(%eax)
...
Disassembly of section .altinstr_replacement:
00000000 <.altinstr_replacement>:
0: 0f ae f8 sfence
3: 0f ae e8 lfence
6: 0f ae e8 lfence
$ readelf -r minimodule.ko
Relocation section '.rel.text' at offset 0xc3e0 contains 7 entries:
Offset Info Type Sym.Value Sym. Name
0000000b 00002402 R_386_PC32 00000000 iowrite32
0000001a 00002402 R_386_PC32 00000000 iowrite32
00000029 00002402 R_386_PC32 00000000 iowrite32
00000038 00002402 R_386_PC32 00000000 iowrite32
0000004c 00002402 R_386_PC32 00000000 iowrite32
00000060 00002402 R_386_PC32 00000000 iowrite32
0000006f 00002402 R_386_PC32 00000000 iowrite32
So it’s in essence the same, only the mcount() call in the beginning was skipped.
The related sources for iowrite32() on ARM
These are the key excerpts from arch/arm/include/asm/io.h:
static inline void __raw_writel(u32 val, volatile void __iomem *addr)
{
asm volatile("str %1, %0"
: "+Qo" (*(volatile u32 __force *)addr)
: "r" (val));
}
...
#define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)
...
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
...
#define iowrite32(v,p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_le32(v), p); })
As for __iowmb(), it goes
/* IO barriers */
#ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE
#include <asm/barrier.h>
#define __iormb() rmb()
#define __iowmb() wmb()
#else
#define __iormb() do { } while (0)
#define __iowmb() do { } while (0)
#endif
so it’s down to the configuration if __iowmb() does something. And to get the full picture, these are snips from arch/arm/include/asm/barrier.h:
#if __LINUX_ARM_ARCH__ >= 7
#define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory")
#define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory")
#define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory")
...
#ifdef CONFIG_ARCH_HAS_BARRIERS
#include <mach/barriers.h>
#elif defined(CONFIG_ARM_DMA_MEM_BUFFERABLE) || defined(CONFIG_SMP)
#define mb() do { dsb(); outer_sync(); } while (0)
#define rmb() dsb()
#define wmb() do { dsb(st); outer_sync(); } while (0)
#else
#define mb() barrier()
#define rmb() barrier()
#define wmb() barrier()
#endif
Results on ARM
This is what the same module compiled for ARM Cortex A9, Little Endian gives (I’ve added extra newlines in the middle for clarity):
minimodule.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <try_iowrite32>:
0: e92d4038 push {r3, r4, r5, lr}
4: f57ff04e dsb st
8: e59f2118 ldr r2, [pc, #280] ; 128 <try_iowrite32+0x128>
c: e1a04002 mov r4, r2
10: e5923018 ldr r3, [r2, #24]
14: e3530000 cmp r3, #0
18: 0a000000 beq 20 <try_iowrite32+0x20>
1c: e12fff33 blx r3
20: e59f3104 ldr r3, [pc, #260] ; 12c <try_iowrite32+0x12c>
24: e59f1104 ldr r1, [pc, #260] ; 130 <try_iowrite32+0x130>
28: e5831678 str r1, [r3, #1656] ; 0x678
2c: f57ff04e dsb st
30: e5942018 ldr r2, [r4, #24]
34: e1a05001 mov r5, r1
38: e1a04003 mov r4, r3
3c: e3520000 cmp r2, #0
40: 0a000000 beq 48 <try_iowrite32+0x48>
44: e12fff32 blx r2
48: e5845678 str r5, [r4, #1656] ; 0x678
4c: f57ff04e dsb st
50: e59f20d0 ldr r2, [pc, #208] ; 128 <try_iowrite32+0x128>
54: e1a04002 mov r4, r2
58: e5923018 ldr r3, [r2, #24]
5c: e3530000 cmp r3, #0
60: 0a000000 beq 68 <try_iowrite32+0x68>
64: e12fff33 blx r3
68: e59f30bc ldr r3, [pc, #188] ; 12c <try_iowrite32+0x12c>
6c: e59f20c0 ldr r2, [pc, #192] ; 134 <try_iowrite32+0x134>
70: e5832678 str r2, [r3, #1656] ; 0x678
74: f57ff04e dsb st
78: e5942018 ldr r2, [r4, #24]
7c: e1a04003 mov r4, r3
80: e3520000 cmp r2, #0
84: 0a000000 beq 8c <try_iowrite32+0x8c>
88: e12fff32 blx r2
8c: e59f30a4 ldr r3, [pc, #164] ; 138 <try_iowrite32+0x138>
90: e5843678 str r3, [r4, #1656] ; 0x678
94: f57ff04e dsb st
98: e59f2088 ldr r2, [pc, #136] ; 128 <try_iowrite32+0x128>
9c: e1a04002 mov r4, r2
a0: e5923018 ldr r3, [r2, #24]
a4: e3530000 cmp r3, #0
a8: 0a000000 beq b0 <try_iowrite32+0xb0>
ac: e12fff33 blx r3
b0: f57ff04e dsb st
b4: e5943018 ldr r3, [r4, #24]
b8: e3530000 cmp r3, #0
bc: 0a000000 beq c4 <try_iowrite32+0xc4>
c0: e12fff33 blx r3
c4: e59f3060 ldr r3, [pc, #96] ; 12c <try_iowrite32+0x12c>
c8: e59f206c ldr r2, [pc, #108] ; 13c <try_iowrite32+0x13c>
cc: e5832678 str r2, [r3, #1656] ; 0x678
d0: f57ff04f dsb sy
d4: f57ff04e dsb st
d8: e59f1048 ldr r1, [pc, #72] ; 128 <try_iowrite32+0x128>
dc: e1a04003 mov r4, r3
e0: e1a05001 mov r5, r1
e4: e5912018 ldr r2, [r1, #24]
e8: e3520000 cmp r2, #0
ec: 0a000000 beq f4 <try_iowrite32+0xf4>
f0: e12fff32 blx r2
f4: e59f3044 ldr r3, [pc, #68] ; 140 <try_iowrite32+0x140>
f8: e5843678 str r3, [r4, #1656] ; 0x678
fc: f57ff05a dmb ishst
100: f57ff04e dsb st
104: e5953018 ldr r3, [r5, #24]
108: e3530000 cmp r3, #0
10c: 0a000000 beq 114 <try_iowrite32+0x114>
110: e12fff33 blx r3
114: e59f3010 ldr r3, [pc, #16] ; 12c <try_iowrite32+0x12c>
118: e59f2024 ldr r2, [pc, #36] ; 144 <try_iowrite32+0x144>
11c: e5832678 str r2, [r3, #1656] ; 0x678
120: f57ff05b dmb ish
124: e8bd8038 pop {r3, r4, r5, pc}
128: 00000000 .word 0x00000000
12c: 12345000 .word 0x12345000
130: abcd0001 .word 0xabcd0001
134: abcd0002 .word 0xabcd0002
138: abcd0003 .word 0xabcd0003
13c: abcd0004 .word 0xabcd0004
140: abcd0005 .word 0xabcd0005
144: abcd0006 .word 0xabcd0006
This was a lot of code (somehow that’s what you get with ARM). There are no calls to iowrite32(), so this is done inline for ARM (consistent with the sources).
This requires some translation from ARM opcodes to human language (taken from this page):
- DSB SY — Data Synchronization Barrier: No instruction in program order after this instruction executes until all explicit memory accesses before this instruction complete, as well as all cache, branch predictor and TLB maintenance operations before this instruction complete.
- DSB ST — Like DSB SY, but waits only for data writes to complete.
- DMB ISHST — Data Memory Barrier, operation that waits only for stores to complete, and only to the inner shareable domain (whatever that “inner shareable domain” is).
- DMB ISH — Data Memory Barrier, operation that waits only to the inner shareable domain.
Now let’s decipher the assembly code, which is quite tangled. Luckily, it’s easy to spot the seven write operations as the seven “str” commands in the assembly code. It’s also easy to see that all each iowrite32() starts with an “dsb st” which forces waiting until previous writes has completed. So each iowrite32() spans from a “dsb st” to a “str”. This matches the definition of iowrite32() as __iowmb() and then __raw_writel(…).
The memory barriers are quite clear too:
- wmb() becomes “dsb st”, the full synchronization barrier for writes (which is also issued automatically before each iowrite32).
- rmb() becomes “dsb sy”, the full synchronization barrier for reads and writes
- smp_wmb() becomes “dmb ishst”, the “inner shareable domain” memory barrier for writes
- smp_rmb() becomes “dmb ish”, the “inner shareable domain” memory barrier for reads and writes
Now with writel()
So I through it would be nice to repeat all this with writel(). Spoiler: Nothing thrilling happens here.
Module code (includes omitted):
void try_writel(void) {
void __iomem *p = (void *) 0x12345678;
writel(0xabcd0001, p);
writel(0xabcd0001, p);
writel(0xabcd0002, p);
mmiowb();
writel(0xabcd0003, p);
wmb();
writel(0xabcd0004, p);
rmb();
writel(0xabcd0005, p);
smp_wmb();
writel(0xabcd0006, p);
smp_rmb();
}
EXPORT_SYMBOL(try_writel);
Assembly on 64-bit Intel:
minimodule.ko: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <try_writel>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: e8 00 00 00 00 callq 9 <try_writel+0x9>
9: b8 01 00 cd ab mov $0xabcd0001,%eax
e: 89 04 25 78 56 34 12 mov %eax,0x12345678
15: 89 04 25 78 56 34 12 mov %eax,0x12345678
1c: b8 02 00 cd ab mov $0xabcd0002,%eax
21: 89 04 25 78 56 34 12 mov %eax,0x12345678
28: b8 03 00 cd ab mov $0xabcd0003,%eax
2d: 89 04 25 78 56 34 12 mov %eax,0x12345678
34: 0f ae f8 sfence
37: b8 04 00 cd ab mov $0xabcd0004,%eax
3c: 89 04 25 78 56 34 12 mov %eax,0x12345678
43: 0f ae e8 lfence
46: b8 05 00 cd ab mov $0xabcd0005,%eax
4b: 89 04 25 78 56 34 12 mov %eax,0x12345678
52: b8 06 00 cd ab mov $0xabcd0006,%eax
57: 89 04 25 78 56 34 12 mov %eax,0x12345678
5e: c9 leaveq
5f: c3 retq
OK, so writel() just translated into a couple of inline “mov” opcodes. There’s even an optimization between the first and second move, so %eax isn’t set twice. Hi-tec, I’m telling you.
And on 32-bit Intel:
minimodule.ko: file format elf32-i386
Disassembly of section .text:
00000000 <try_writel>:
0: b8 01 00 cd ab mov $0xabcd0001,%eax
5: a3 78 56 34 12 mov %eax,0x12345678
a: a3 78 56 34 12 mov %eax,0x12345678
f: b0 02 mov $0x2,%al
11: a3 78 56 34 12 mov %eax,0x12345678
16: b0 03 mov $0x3,%al
18: a3 78 56 34 12 mov %eax,0x12345678
1d: f0 83 04 24 00 lock addl $0x0,(%esp)
22: b0 04 mov $0x4,%al
24: a3 78 56 34 12 mov %eax,0x12345678
29: f0 83 04 24 00 lock addl $0x0,(%esp)
2e: b0 05 mov $0x5,%al
30: a3 78 56 34 12 mov %eax,0x12345678
35: b0 06 mov $0x6,%al
37: a3 78 56 34 12 mov %eax,0x12345678
3c: f0 83 04 24 00 lock addl $0x0,(%esp)
41: c3 ret
...
Disassembly of section .altinstr_replacement:
00000000 <.altinstr_replacement>:
0: 0f ae f8 sfence
3: 0f ae e8 lfence
6: 0f ae e8 lfence
And for ARM, it’s exactly the same code (to the byte) as iowrite32() is an alias for writel(). But I listed it here anyhow for those who don’t take my word for it:
minimodule.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <try_writel>:
0: e92d4038 push {r3, r4, r5, lr}
4: f57ff04e dsb st
8: e59f2118 ldr r2, [pc, #280] ; 128 <try_writel+0x128>
c: e1a04002 mov r4, r2
10: e5923018 ldr r3, [r2, #24]
14: e3530000 cmp r3, #0
18: 0a000000 beq 20 <try_writel+0x20>
1c: e12fff33 blx r3
20: e59f3104 ldr r3, [pc, #260] ; 12c <try_writel+0x12c>
24: e59f1104 ldr r1, [pc, #260] ; 130 <try_writel+0x130>
28: e5831678 str r1, [r3, #1656] ; 0x678
2c: f57ff04e dsb st
30: e5942018 ldr r2, [r4, #24]
34: e1a05001 mov r5, r1
38: e1a04003 mov r4, r3
3c: e3520000 cmp r2, #0
40: 0a000000 beq 48 <try_writel+0x48>
44: e12fff32 blx r2
48: e5845678 str r5, [r4, #1656] ; 0x678
4c: f57ff04e dsb st
50: e59f20d0 ldr r2, [pc, #208] ; 128 <try_writel+0x128>
54: e1a04002 mov r4, r2
58: e5923018 ldr r3, [r2, #24]
5c: e3530000 cmp r3, #0
60: 0a000000 beq 68 <try_writel+0x68>
64: e12fff33 blx r3
68: e59f30bc ldr r3, [pc, #188] ; 12c <try_writel+0x12c>
6c: e59f20c0 ldr r2, [pc, #192] ; 134 <try_writel+0x134>
70: e5832678 str r2, [r3, #1656] ; 0x678
74: f57ff04e dsb st
78: e5942018 ldr r2, [r4, #24]
7c: e1a04003 mov r4, r3
80: e3520000 cmp r2, #0
84: 0a000000 beq 8c <try_writel+0x8c>
88: e12fff32 blx r2
8c: e59f30a4 ldr r3, [pc, #164] ; 138 <try_writel+0x138>
90: e5843678 str r3, [r4, #1656] ; 0x678
94: f57ff04e dsb st
98: e59f2088 ldr r2, [pc, #136] ; 128 <try_writel+0x128>
9c: e1a04002 mov r4, r2
a0: e5923018 ldr r3, [r2, #24]
a4: e3530000 cmp r3, #0
a8: 0a000000 beq b0 <try_writel+0xb0>
ac: e12fff33 blx r3
b0: f57ff04e dsb st
b4: e5943018 ldr r3, [r4, #24]
b8: e3530000 cmp r3, #0
bc: 0a000000 beq c4 <try_writel+0xc4>
c0: e12fff33 blx r3
c4: e59f3060 ldr r3, [pc, #96] ; 12c <try_writel+0x12c>
c8: e59f206c ldr r2, [pc, #108] ; 13c <try_writel+0x13c>
cc: e5832678 str r2, [r3, #1656] ; 0x678
d0: f57ff04f dsb sy
d4: f57ff04e dsb st
d8: e59f1048 ldr r1, [pc, #72] ; 128 <try_writel+0x128>
dc: e1a04003 mov r4, r3
e0: e1a05001 mov r5, r1
e4: e5912018 ldr r2, [r1, #24]
e8: e3520000 cmp r2, #0
ec: 0a000000 beq f4 <try_writel+0xf4>
f0: e12fff32 blx r2
f4: e59f3044 ldr r3, [pc, #68] ; 140 <try_writel+0x140>
f8: e5843678 str r3, [r4, #1656] ; 0x678
fc: f57ff05a dmb ishst
100: f57ff04e dsb st
104: e5953018 ldr r3, [r5, #24]
108: e3530000 cmp r3, #0
10c: 0a000000 beq 114 <try_writel+0x114>
110: e12fff33 blx r3
114: e59f3010 ldr r3, [pc, #16] ; 12c <try_writel+0x12c>
118: e59f2024 ldr r2, [pc, #36] ; 144 <try_writel+0x144>
11c: e5832678 str r2, [r3, #1656] ; 0x678
120: f57ff05b dmb ish
124: e8bd8038 pop {r3, r4, r5, pc}
128: 00000000 .word 0x00000000
12c: 12345000 .word 0x12345000
130: abcd0001 .word 0xabcd0001
134: abcd0002 .word 0xabcd0002
138: abcd0003 .word 0xabcd0003
13c: abcd0004 .word 0xabcd0004
140: abcd0005 .word 0xabcd0005
144: abcd0006 .word 0xabcd0006
Overview
Having a Lenovo Yoga 2 13″ (non-pro) running Ubuntu 14.04.1, I couldn’t get Wireless LAN up and running, as the WLAN NIC appeared to be “hardware locked”. This is the summary of how I solved this issue. If you’re not interested in the gory details, you may jump right to bottom, where I offer a replacement module that fixes it. At least for me.
Environment details: Distribution kernel 3.13.0-32-generic on an Intel i5-4210U CPU @ 1.70GHz. The Wifi device is an Intel Dual Band Wireless-AC 7260 (8086:08b1) connected to the PCIe bus, taken care of by the iwlwifi driver.
The problem
Laptops have a mechanism for working in “flight mode” which means turning off any device that could emit RF power, so that the airplane can crash for whatever different reason. Apparently, some laptops have a physical on-off switch to request this, but on Lenovo Yoga 13, the arrangement is to press a button on the keyboard with an airplane drawn on it. The one shared with F7.
It seems to be, that on Lenovo Yoga 13, the ACPI interface, which is responsible for reporting the Wifi’s buttons state, always reports that it’s in flight mode. So Linux turns off Wifi, and on the desktop’s Gnome network applet it says “Wi-Fi is disabled by hardware switch”.
In the dmesg log one can tell the problem with a line like
iwlwifi 0000:01:00.0: RF_KILL bit toggled to disable radio.
which is issued by the interrupt request handler defined in drivers/net/wireless/iwlwifi/pcie/rx.c, which responds to an interrupt from the device that informs the host that the hardware RF kill bit is set. So the iwlwifi module is not to blame here — it just responds to a request from the ACPI subsystem.
rfkill
The management of RF-related devices is handled by the rfkill subsystem. On my laptop, before solving the problem, a typical output went
$ rfkill list all
0: ideapad_wlan: Wireless LAN
Soft blocked: yes
Hard blocked: yes
1: ideapad_bluetooth: Bluetooth
Soft blocked: no
Hard blocked: yes
6: hci0: Bluetooth
Soft blocked: no
Hard blocked: no
7: phy1: Wireless LAN
Soft blocked: yes
Hard blocked: yes
So there are different entities that can be controlled with rfkill, enumerated and assigned soft and hard blocks. Each of these relate to a directory in /sys/class/rfkill/. For example, the last device, “phy7″ enumerated as 7 corresponds to /sys/class/rfkill/rfkill7, where the “hard” and “soft” pseudo-files signify the status with “0″ or “1″ values.
The soft block can be changed by “rfkill unblock 0″ or “rfkill unblock 7″, but this doesn’t really help with the hardware block. Both has to be “off” to use the device.
As can be seen easily from the rkfill list above, each of the physical devices are registered twice as rfkill devices: Once by their driver, and a second time by the ideapad_laptop driver. This will be used in the solution below.
The ideapad_laptop module
The ideapad-laptop module is responsible for talking with the ACPI layer on machines that match “VPC2004″ as a platform (as in /sys/devices/platform/VPC2004:00, or /sys/bus/acpi/devices/VPC2004:00, but doesn’t fit anything found in /sys/class/dmi/id/).
Blacklisting this module has been suggested for Yoga laptops all over the web. In particular this post suggests to insmod the module once with a hack that forces the Wifi on, and then blacklist it.
But by blacklisting ideapad-laptop, the computer loses some precious functionality, including disabling Wifi and the touchpad by pressing a button. So this is not an appealing solution.
Ideapad’s two debugfs output files go:
# cat /sys/kernel/debug/ideapad/cfg
cfg: 0x017DE014
Capability: Bluetooth Wireless Camera
Graphic:
# cat /sys/kernel/debug/ideapad/status
Backlight max: 16
Backlight now: 9
BL power value: On
=====================
Radio status: Off(0)
Wifi status: Off(0)
BT status: On(1)
3G status: Off(0)
=====================
Touchpad status:Off(0)
Camera status: On(1)
So the Radio and Wifi statuses, which are read from the ACPI registers, are off. This makes the ideapad_laptop module conclude that everything should go off.
The solution
In essence, the solution for the problem is to take the ideapad_laptop’s hands off the Wifi hardware, except for turning the hardware block off when it’s loaded. It consists of making the following changes in drivers/platform/x86/ideapad-laptop.c:
- First, remove the driver’s rfkill registration. Somewhere at the beginning of the file, change
#define IDEAPAD_RFKILL_DEV_NUM (3)
to
#define IDEAPAD_RFKILL_DEV_NUM (2)
and in the definition of ideapad_rfk_data[], remove the line saying
{ "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN }
This prevents the driver from presenting an rfkill interface, so it keeps its hands off.
- There is however a chance that the relevant bit in the ACPI layer already has the hardware block on. So let’s turn it off every time the driver loads. In ideapad_acpi_add(), after the call to ideapad_sync_rfk_state(), more or less, add the following two lines:
pr_warn("Hack: Forcing WLAN hardware block off\n");
write_ec_cmd(priv->adev->handle, VPCCMD_W_WIFI, 1);
- And finally, solve a rather bizarre phenomenon, that when reading for the RF state with a VPCCMD_R_RF command, the Wifi interface is hardware blocked for some reason. Note that radio is always in off mode, so it’s a meaningless register on Yoga 2. This is handled in two places. First, empty ideapad_sync_rfk_state() completely, by turning it into
static void ideapad_sync_rfk_state(struct ideapad_private *priv)
{
}
This function reads VPCCMD_R_RF and calls rfkill_set_hw_state() accordingly, but on Yoga 2 it will always block everything, so what’s the point?
Next, in debugfs_status_show() which prints out /sys/kernel/debug/ideapad/status, remove the following three lines:
if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
seq_printf(s, "Radio status:\t%s(%lu)\n",
value ? "On" : "Off", value);
Having these changes made, the Wifi works properly, regardless of it was previously reported hardware blocked.
This can’t be submitted as a patch to the kernel, because presumably some laptops need the rfkill interface for Wifi through ideapad_laptop (or else, why was it put there in the first place?).
Also, maybe I should have done this for Bluetooth too? Don’t know. I don’t use Bluetooth right now, and the desktop applet seems to say all is fine with it anyhow.
Download the driver fix
For the lazy ones, I’ve prepared a little kit for compiling the relevant driver. I’ve taken the driver as it appears in kernel 3.16, more or less, and applied the changes above. And I then added a Makefile to make it compile easily. Since the kernel API changes rather rapidly, this will probably work well for kernels around 3.16 (that includes 3.13), and then you’ll have to apply the changes manually. If it isn’t fixed in the kernel itself by then.
Download it from here, unzip it, change directory, and compile it with typing “make”. This works only if you have the kernel headers and gcc compiler installed, which is usually the case in recent distributions. So a session like this is expected:
$ make
make -C /lib/modules/3.13.0-32-generic/build SUBDIRS=/home/eli/yoga-wifi-fix modules
make[1]: Entering directory `/usr/src/linux-headers-3.13.0-32-generic'
CC [M] /home/eli/yoga-wifi-fix/ideapad-laptop.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/eli/yoga-wifi-fix/ideapad-laptop.mod.o
LD [M] /home/eli/yoga-wifi-fix/ideapad-laptop.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.13.0-32-generic'
Then replace the fresh ideapad-laptop.ko with the one the kernel uses. First, let’s figure out where to. The modinfo command help here:
$ modinfo ideapad_laptop
filename: /lib/modules/3.13.0-32-generic/kernel/drivers/platform/x86/ideapad-laptop.ko
license: GPL
description: IdeaPad ACPI Extras
author: David Woodhouse <dwmw2@infradead.org>
srcversion: BA339D663FA3B10105A1DC0
alias: acpi*:VPC2004:*
depends: sparse-keymap
vermagic: 3.13.0-32-generic SMP mod_unload modversions
parm: no_bt_rfkill:No rfkill for bluetooth. (bool)
So the directory is now known (marked in red). This leaves us with copying it into the right place:
$ sudo cp ideapad-laptop.ko /lib/modules/3.13.0-32-generic/kernel/drivers/platform/x86/
The new module is valid on the next reboot. Or the next insmod/modprobe, if you’re have the same allergy as myself regarding rebooting a Linux system.
The idea is to take a mail that has already been send (and is hence in the “sent” folder and send it again with sendmail. Why? In my case the idea is that Thunderbird and sendmail connect to different relay servers, and the one used by Thunderbird 3.0.7 is blacklisted by the destination (I got a reject message).
It’s simple: Find the message in Thunderbird’s “Sent” folder, and save it as an .eml file, say, Trying.eml.
Possibly edit the file, and remove the first three lines (even though there’s probably no problem leaving them there):
X-Mozilla-Status: 0001
X-Mozilla-Status2: 00800000
X-Mozilla-Keys:
Possibly add yourself as a Bcc: after the From: line with
Bcc: Myself <myself@example.com>
And then send the message with
$ sendmail -t < Trying.eml
The -t flag means to find the recipient’s address in the message’s body, which is usually what we want.
I ran into a weird problem while attempting to enable SDMA for UARTs on an i.MX53 processor running Freescale’s 2.6.35.3 Linux kernel: To begin with, the UART would only transmit 48 bytes, which is probably a result of only one watermark event arriving (the initial kickoff filled the UART’s FIFO with 32 bytes, and then one SDMA event occurred when the FIFO reached 16 bytes’ fill, so another 16 bytes were sent).
So it seemed like the SDMA core misses the UART’s watermark events. More scrutinized experiments with my own test scripts revealed a variety of weird behaviors, including what appeared to be preemption of the SDMA script’s process, even though the reference manual is quite clear about it: Context switching of SDMA scripts is voluntary. And still, the flow of data on the UART’s tx lines was stopped for 5-6 ms periods randomly, even when I ran a busy-wait loop in the SDMA script, polling the “not full” flag of the UART’s transmission FIFO.
So it looked like something stopped the SDMA script from running in the middle of the loop (which included no “yield” nor “done” command). Or maybe a completely different issue? Maybe the peripheral bus wasn’t completely coherent? Anything seemed possible at some point.
As the title implies, the problem was power management, and poor settings of the SDMA’s behavior during low power modes.
It goes like this: Every time the Linux kernel’s scheduler has no process to run, it executes an WFI ARM processor command, halting the processor until an interrupt arrives (from a peripheral or just the scheduler’s tick clock). But before doing that, the kernel calls an architecture-dependent function, arch_idle(), which possibly shuts down or slows down clocks in order to increase power savings.
The kernel I used didn’t configure the SDMA’s behavior in the lower-power WAIT mode correctly, causing it halt and miss events while the processor was in this mode. The word is that to overcome this, the CCM_CCGR bits for SDMA clocks should be set to 11 (bits 31-30 in CCM_CCGR4). There is probably also a need to enable aips_tz1_clk to keep the SDMA and aips_tz1 clocks running. But since the application I worked on didn’t have any power restrictions, I decided to avoid these power mode switches altogether.
This was done by editing arch/arm/mach-mx5/system.c in the kernel tree, where it said:
void arch_idle(void)
{
if (likely(!mxc_jtag_enabled)) {
if (ddr_clk == NULL)
ddr_clk = clk_get(NULL, "ddr_clk");
if (gpc_dvfs_clk == NULL)
gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk");
/* gpc clock is needed for SRPG */
clk_enable(gpc_dvfs_clk);
mxc_cpu_lp_set(arch_idle_mode);
and delete the last line in the listing above — the call to mxc_cpu_lp_set(), which changes the processor’s power mode.
This solved the SDMA problem for me.
As a matter of fact, I would suggest commenting out this line during the development phase of any i.MX-based system, and return it once everything works. True, this shouldn’t be an issue if the clocks are properly configured. But if they’re not, something will fail, and the natural tendency is to focus the drivers of the failing functionality, and not looking for power management issues.
When the power reduction function is re-enabled at some later point, it’s quite evident what the problem is, if something fails then. So even if the target product is battery-driven, do yourself a favor, and drop that line in system.c until you’re finished struggling with other things.
Running Xillinux on the Zybo board, this is how I toggled a GPIO pin from a plain one-liner bash script in Linux. The same technique can be used for other Zynq-7000 boards (Zedboard in particular) to easily control GPIO pins.
First, I looked up which GPIO pin it is. The pin assignments can be found in the FPGA bundle, in xillydemo.ucf (or in xillydemo.sdc, if Vivado was used to build the project).
So I choose to connect to PMOD header JB, first pin, and the PMOD’s GND.
In the UCF file there’s a line saying
## Pmod Header JB
NET PS_GPIO[32] LOC=T20 | IOSTANDARD=LVCMOS33; #IO_L15P_T2_DQS_34
and its counterpart in the SDC file is
## Pmod Header JB
set_property -dict "PACKAGE_PIN T20 IOSTANDARD LVCMOS33" [get_ports "PS_GPIO[32]"]
So it’s quite clear and cut that the PS_GPIO[32] signal is connected to PMOD B. It doesn’t hurt taking a look on the board’s schematics as well, if you’re convenient with those drawings, and see that the Zynq device’s pin T20 indeed goes to PMOD B, and which pin.
Hooked up as shown in this pic (click to enlarge):

The offset between PS_GPIO numbers and those designated by Linux is 54. So this pin is found as number 32+54=86.
Hence
# echo 86 > /sys/class/gpio/export
# echo out > /sys/class/gpio/gpio86/direction
And then poor man’s oscillator:
# while [ 1 ] ; do echo 1 > /sys/class/gpio/gpio86/value ; echo 0 > /sys/class/gpio/gpio86/value ; done
This runs at a staggering 2.9 kHz. Pretty impressive for the slowest form of programming one can think about.
So I installed Vivado on my Centos 6.5 64-bit Linux machine, and even though it promised to install icons on my desktop, it didn’t. This is how I installed them manually. There is surely a simpler way, as the special launch bash scripts I created must be somewhere. But I didn’t bother looking.
So it consists of generating four files, all in all, as follows.
First, as root, create these two files, and make them executable by all:
/usr/local/bin/run-vivado as follows:
#!/bin/bash
. /opt/Xilinx/Vivado/2014.1/settings64.sh
vivado &
And /usr/local/bin/run-sdk:
#!/bin/bash
. /opt/Xilinx/SDK/2014.1/settings64.sh
xsdk &
The path to Xilinx’ installation is /opt/Xilinx, of course. Adjust this to where your installation was made, and you should pick the settings32.sh file if you’re running on a 32-bit machine.
And next, we have the launchers, both to be placed in the Desktop directory of the ordinary user who should have these on the desktop.
The file named “Vivado 2014.1.desktop” goes
[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Icon=/opt/Xilinx/Vivado/2014.1/doc/images/vivado_logo.ico
Name[en_US]=Vivado 2014.1
Exec=/usr/local/bin/run-vivado
Path=/home/myself/vivado-outputs/
Name=Vivado 2014.1
StartupNotify=true
and “Xilinx SDK.desktop” is
[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Icon=/opt/Xilinx/SDK/2014.1/data/sdk/images/sdk_logo.ico
Name[en_US]=Xilinx SDK
Exec=/usr/local/bin/run-sdk
Name=Xilinx SDK
StartupNotify=true
I’ve marked the StartupNotify assignment in red, because this is what makes the mouse pointer turn into “busy” when the program is launched, until the splash window appears. It’s important for Vivado in particular, which takes some time to start up.
Also, the Path assignment in the Vivado launcher sets the directory at which Vivado runs, which should be changed to a directory that exists, and is a convenient place to dump all log files that Vivado generates.
A list of possible assignments in desktop launchers can be found on this page.
Background
This is yet another war story about making the FSBL boot on a Zynq processor.
I had prepared an FSBL for a certain target using SDK 14.6, and then someone needed it in a Vivado package, using the SDK attached to Vivado 2014.1. In a perfect world, I would have exported the system’s configuration from XPS 14.6 to Vivado as an XML file, and generated the FSBL there. But experience shows that nothing really guarantees that the processor’s configuration will be adopted correctly in Vivado. As a matter of fact, I’ve seen that Vivado imports some parameters, and others are ignored.
But hey, I could just copy the existing FSBL source files to a new workspace in the target SDK? After all, it’s just C code!
This is in fact possible, going File > Import… > General > Existing Projects into Workspace. Then navigate to the path of the original project’s workspace. And don’t forget marking “Copy projects into workspace” so that the old one can be moved or deleted. A popup will allow selecting which projects to import, and it’s done!
Well, not. Selecting the three projects in an FSBL source set (fsbl, fsbl_bsp and system_hw_platform) will indeed create a fresh FSBL project, but it fails compiling (saying that it can’t find libxilffs as required by the -lxilffs or something like that).
To work around this, I imported only the system_hw_platform project, and generated the FSBL project in Vivado’s SDK, as usual: File > New > Application Project. Set the name to “fsbl”, make sure that the underlying hardware project it system_hw_platform. Click “Next” and pick “Zynq FSBL” as the template.
This makes sense, because the FSBL project relies on the C sources that were generated when XPS exported the project to SDK. So the hardware configuration remains correct, and the FSBL is new. No reason why this shouldn’t work, in theory.
The project compiled right away, and an fsbl.elf was ready for mixing into a boot.bin file.
Hurray! Not. It didn’t boot.
Despair not
The immediate measure for these cases in compiling the FSBL with the -DFSBL_DEBUG compilation parameter (which defines the FSBL_DEBUG compilation variable, turning on debug messages). With some luck, something informative will show up on the serial console, even if it appeared dead before.
I was one of those lucky bas#$%*s. I got:
PS7_INIT_FAIL : PS7 initialization successful
FSBL Status = 0xA012
Hmmm… That sounds like a mixed-up error message. It failed because it was successful? Well, in fact, the message itself represents the confusion causing the problem.
The FSBL status 0xA012 is returned when the call to ps7_init() fails in main.c. Or more precisely, when the returned value isn’t FSBL_PS7_INIT_SUCCESS. By the way, the FSBL generated by SDK 14.6 doesn’t even bother to check the return value of ps7_init(), but that’s irrelevant here.
Anyhow, note that ps7_init() is defined in the system_hw_platform, which consists of sources generated by XPS 14.6, but called by the FSBL, which was generated by Vivado.
This is a bit delicate, because ps7_init() returns PS7_INIT_SUCCESS when successful (see ps7_init.c), which happens to be defined in ps7_init.h as
#define PS7_INIT_SUCCESS (0) // 0 is success in good old C
and non-zero values meaning failure. This is the classic UNIX convention.
For some reason, this is what one finds in fsbl.h:
#ifdef NEW_PS7_ERR_CODE
#define FSBL_PS7_INIT_SUCCESS PS7_INIT_SUCCESS
#else
#define FSBL_PS7_INIT_SUCCESS (1)
#endif
In short: FSBL_PS7_INIT_SUCCESS=1, PS7_INIT_SUCCESS=0. A problem indeed.
So this is a direct consequence of mixing an old hardware project with a new FSBL. They changed the error code values somewhere in the middle.
Solution
The clean way to fix this is defining NEW_PS7_ERR_CODE during compilation. The less clean method is just remove this #ifdef statement and leave it as
#define FSBL_PS7_INIT_SUCCESS PS7_INIT_SUCCESS
And with this FSBL booted correctly and all was well.
I know that getting the FSBL to boot is a recurring problem. Please don’t turn to me for help if your board doesn’t boot — there’s no secret trick, just good old debugging that takes time and effort.