Friday, May 4, 2018

Yocto kernel in-tree development work-flows

The Yocto Project Linux Kernel Development Manual, covers at least two flows for kernel development - 'Traditional Kernel Development' and 'devtool'. Here, I'll present some experiences with both these methods, along with a third method that I found convenient.

Traditional Kernel Development
Preparation
- create a layer for holding kernel patches, and configuration fragments
- create an append recipe (eg. meta-mylayer/recipes-kernel/linux/linux-yocto_4.12.bbappend) that identifies the patches and configuration fragments that you wish to apply
- create a local clone of the Yocto Linux Kernel

Development Loop
- Make code and/or configuration changes in local clone of Yocto Linux Kernel
- Stage and commit the changes (note this is necessary to have them included in the build)
- Adjust conf/local.conf to point to your local kernel clone
- Build and test the changes
- Generate a patch/config fragment
- Move the patch file to your layer, and update the .bbappend file to use it

Reflecting on the flow
Despite the efficiency of git's 'amend commit' function, the loop is quite heavy/time consuming.
For example, if you find the need to add a few printk debug statements, for troubleshooting, committing them each time before building seems an unnecessary burden.

Devtool Kernel Development
Preparation
- create a layer for holding kernel patches
- create an append recipe (eg. meta-mylayer/recipes-kernel/linux/linux-yocto_4.12.bbappend) that identifies the patches that you wish to apply
- (build and install an extensible SDK)
- build a clean image
- checkout the kernel source using 'devtool modify linux-yocto'. This step creates a local copy of the kernel source, in the (SDK) workspace, as well a recipe to include it in the build

Development Loop
- Make code changes in the SDK workspace clone of the kernel source tree
- Build and test the changes
- Stage and commit the changes
- Use 'devtool finish' to generate patches, and include them in your layer

Reflecting on the flow
This flow address the burdens of the traditional flow, as the stage/commit/patch steps are moved outside the change-build-test loop. I found this works well for source code changes.

A limitation of the flow, is that configuration fragments and patches are 'locked' during the preparation step of the flow. Ie. if, whilst in the development loop, you wish to add a configuration fragment ('from the shelf'), you should exit the flow, add the fragment, and re-enter the flow by again completing the preparation steps.

Alternatively, one can manually apply the fragments/patches in the workspace - but doing so conflates these changes with those being made in the development loop.

External Source Kernel Development
Preparation
- create a local clone of the Yocto Linux Kernel
- create a layer for holding kernel patches, and configuration fragments
- create an append recipe (eg. meta-mylayer/recipes-kernel/linux/linux-yocto_4.12.bbappend) that identifies the patches and configuration fragments that you wish to apply
- in the .bbappend recipe add the following -
inherit externalsrc
EXTERNALSRC = "/repo/linux-custom"
SRCTREECOVEREDTASKS := "do_validate_branches do_kernel_checkout do_fetch"

Development Loop
- Make code and/or configuration changes in the kernel source tree
- Build and test the changes
- Stage and commit the changes
- Generate a patch/config fragment
- Move the patch file to your layer, and update the .bbappend file to use it
- Remove the references to the local clone of the Yocto Linux Kernel when done

Reflecting on the flow
This flow attempts to combine the best parts of the other two flows.

It 'gives back' the capability to add/remove patches/configuration fragments via the meta layer (aka. recipe), by overriding this definition in the kernel-yocto.bbclass -
SRCTREECOVEREDTASKS += "do_kernel_configme do_validate_branches do_kernel_configcheck do_kernel_checkout do_fetch do_unpack do_patch"

Thus allowing patches and config fragements to be applied by running the 'unpack' task when building. ie.
bitbake -C unpack linux-yocto

It retains the simple change-build-test loop of the devtool flow.
It sacrifices the automated update of the meta data, provided by 'devtool finish'.

Friday, April 27, 2018

Yocto - update kernel command line in image

The kernel command line is part of the machine configuration.

If you wish to add to it 'manually', you can use the APPEND variable, in for example your conf/local.conf

APPEND += "oops=panic panic=5"

Then, depending on which boot mechanism you're using, you will need to force building of the boot configuration, to include the change.

eg.
$ bitbake -C populate_sysroot grub-efi

$ grep panic build/tmp/work/genericx86_64-poky-linux/core-image-minimal/1.0-r0/core-image-minimal-1.0/hddimg/EFI/BOOT/grub.cfg
linux /vmlinuz LABEL=boot root=/dev/ram0 oops=panic panic=5 

Unfortunately, as is sometimes the case with Yocto, the left hand doesn't know what the right is doing.

$ grep cmdline_append  build/tmp/deploy/images/genericx86-64/core-image-minimal-genericx86-64.qemuboot.conf

qb_kernel_cmdline_append = vga=0 uvesafb.mode_option=640x480-32 oprofile.timer=1 uvesafb.task_timeout=-1

This likely needs tweaking below -

$ grep QB_KERNEL_CMDLINE_APPEND meta/conf/machine/include/qemuboot-x86.inc
QB_KERNEL_CMDLINE_APPEND = "vga=0 uvesafb.mode_option=${UVESA_MODE} oprofile.timer=1 uvesafb.task_timeout=-1"

Similarly, the append is not present, in the wic image -

$ bitbake -C image_wic core-image-minimal 

$ sudo losetup -o $((2048*512)) --show -f core-image-minimal-genericx86-64.wic
/dev/loop1
$ mkdir /mnt/loop1
$ sudo mount /dev/loop1 /mnt/loop1

$ cat /mnt/loop1/EFI/BOOT/grub.cfg 
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
default=boot
timeout=5
menuentry 'boot'{
linux /bzImage root=PARTUUID=c496bfa5-a796-473c-bb6f-50d7b0161295 rootwait rootfstype=ext4 console=ttyS0,115200 console=tty0
}

This can be tweaked in the 'kickstart' file below -

$ grep append meta-yocto-bsp/wic/genericx86.wks
bootloader --ptable gpt --timeout=5 --append="rootfstype=ext4 console=ttyS0,115200 console=tty0"


Tuesday, October 31, 2017

Yocto Project - dev manual log


Customizing images
- local.conf
  IMAGE_INSTALL_append
  add strace, rebuild, test - seemed to result in a significant rebuild
  real time to do 'nothing' rebuild = 28 seconds
  real time to add 'pciutils, usbutils' rebuild = 7 minutes
  real time to add 'kbd, kbd-keymaps' rebuild = 5:30 minutes
- IMAGE_FEATURES and EXTRA_IMAGE_FEATURES
  in conf/local.conf "read-only-rootfs empty-root-password allow-empty-password"
- Custom .bb
  Move above changes into meta/recipes-core/images/core-image-steve.bb
  rebuild, test - quick.
  Add image feature ssh-server-dropbear
Writing a new recipe
- Creating the Base Recipe Using devtool add
  Follow the autotools amhello example, to create an application, store in git repo locally
  Run 'devtool add amhello /mnt/ee/repo/amhello' -> recipe in workspace
  Run 'devtool build-image core-image-steve' to build image including recipes from workspace
  deployed in /usr/bin/hello
- Creating a layer in which to put the recipe
  bitbake-layers create-layer ../meta-steve
  bitbake-layers add-layer ../meta-steve
- Move recipe to layer
  devtool finish amhello meta-steve
- Re-build image
  Add amhello to core-image-steve.bb
  gotcha - the recipe is missing the 'S = "${WORKDIR}/git"' line. This results in no build


Thursday, May 25, 2017

Headless ubuntu server install (EFI)

Installing ubuntu server on a UEFI machine, via serial console only

Create USB stick (real or for qemu)

$ qemu-img create -f raw ubuntu_efi_inst.img 2G
Formatting 'ubuntu_efi_inst.img', fmt=raw size=2147483648

Create an EFI System Partition (ESP) on it

[steve@steve-GA-880GMA-UD2H archlinux]$ gdisk ubuntu_efi_inst.img 
GPT fdisk (gdisk) version 1.0.1

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries.

Command (? for help): n
Partition number (1-128, default 1): 
First sector (34-4194270, default = 2048) or {+-}size{KMGTP}: 
Last sector (2048-4194270, default = 4194270) or {+-}size{KMGTP}: 
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): ef00
Changed type of partition to 'EFI System'

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to ubuntu_efi_inst.img.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
The operation has completed successfully.

Format it

$ sudo losetup --partscan --show --find ubuntu_efi_inst.img 
/dev/loop3

$ sudo mkfs -t vfat /dev/loop3p1
mkfs.fat 3.0.28 (2015-05-16)
unable to get drive geometry, using default 255/63

Mount it

$ sudo mkdir /mnt/target
$ sudo mount /dev/loop3p1 /mnt/target

Loop mount the installation iso

$ sudo mount -o loop Downloads/ubuntu-16.04.2-server-amd64.iso /mnt/loop/
mount: /dev/loop4 is write-protected, mounting read-only

Copy files needed for EFI boot

$ sudo cp -vr /mnt/loop/EFI /mnt/target/
$ sudo cp -vr /mnt/loop/boot /mnt/target/
$ sudo cp -vr /mnt/loop/install /mnt/target/

Copy the ISO on to it (work-around for installer issues)

Copy the iso file onto the partition, needed during ubuntu install to mount CD
$ sudo cp Downloads/ubuntu-16.04.2-server-amd64.iso /mnt/target/

Patch grub and linux cmd line, to use serial console

$ sudo vi /mnt/target/boot/grub/grub.cfg

#add the lines below, at the top of the file
serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1
terminal_input --append serial
terminal_output --append serial

#subsequent terminal entries need to also use --append eg.

terminal_output --append gfxterm

#edit any wanted linux entries to use console as below
linux /install/vmlinuz  file=/cdrom/preseed/ubuntu-server.seed earlycon=uart,io,0x3f8 earlycon=uart,io,0x3f8 console=uart,io,0x3f8 ---

Boot from the prepared drive

#now boot using qemu or real system (with mnt/target written to usb storage device)
#for qemu we need to make a destination for the ubuntu install

$ qemu-img create -f qcow2 ubuntu2.qcow2 16G
Formatting 'ubuntu2.qcow2', fmt=qcow2 size=17179869184 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16

#optionally run with -nographic,or switch to serial view (ctrl-alt-3)
sudo qemu-system-x86_64 -smp 2 -enable-kvm -m 1G -hda ubuntu2.qcow2 -drive file="ubuntu_efi_inst.img",index=1,media=disk,format=raw -bios /usr/share/edk2/ovmf/OVMF_CODE.fd -nographic

Ubuntu installation process begins

Complete localization options

# on seeing error "Your installation CD-ROM couldn't be mounted."
# skip retry, exit to shell
# mount disk

~ # mount -t vfat /dev/sdb1 /media
~ # mount -o loop /media/ubuntu-16.04.2-server-amd64.iso /cdrom
~ # exit

# now detect CD-ROM
# don't unmount partitions that are in use (the CD is mounted from here)
# do guided install on other disk (sda in this example)
# after the step 'Install grub bootloader' you should get the 'Installation complete' message
# this is a good point to drop back into the shell, and review what has been done

Review grub setup on target drive

~ # cat /target/boot/efi/EFI/ubuntu/grub.cfg 
search.fs_uuid 6b25fe16-1db7-49c2-91a8-15bd590dade8 root hd0,gpt2 
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg

~ # grep terminal /target/boot/grub/grub.cfg 
terminal_input --append serial
terminal_output --append serial
terminal_output --append gfxterm
~ # grep serial /target/boot/grub/grub.cfg 
serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1
terminal_input --append serial
terminal_output --append serial
~ # grep console /target/boot/grub/grub.cfg 
linux /boot/vmlinuz-4.4.0-62-generic.efi.signed root=/dev/sda2 ro earlycon=uart,io,0x3f8 console=uart,io,0x3f8  

#now exit, and reboot
#good luck!

Tuesday, April 18, 2017

HC-06 Bluetooth Module

Based on http://www.martyncurrey.com/arduino-with-hc-05-bluetooth-module-at-mode/

Default mode

Note, use 'software serial' mode on Arduino, as regular uart (pins 0, 1) are also used for USB communication (needed by the IDE).

In the example use pins 2, 3 for RX/TX.
Use voltage divider to get 5v output from Arduino to 3.3v safe for RX on HC-05.

On power up, 2 blinks per second = disconnected
Connect using android device, with 'Serial Bluetooth Terminal' app
Pair device, then connect.
Now should have a steady led
Use code from - https://github.com/bugwhine/arduino/blob/master/bluetooth/bluetooth.ino
Default baud rate for bluetooth side is 9600
From arduino IDE, run program, and open serial monitor
Should be possible to communicate between arduino serial monitor and bluetooth serial app on phone


[steve Arduino]$ hcitool scan
Scanning ...
98:D3:32:20:87:15 HC-06
[steve Arduino]$ sudo rfcomm bind 0 98:D3:32:20:87:15 1
[steve Arduino]$ sudo minicom -s
Serial Port Setup
A -    Serial Device      : /dev/rfcomm0
E  -    Bps/Par/Bits       
C:   9600

Serial comms should now work between minicom, and the arduino serial monitor.

nodejs

sudo dnf install bluez-libs-devel
sudo npm -g install bluetooth-serial-port


node btserial.js
connected
Received: Size of data buf = 1
h
Received: Size of data buf = 6
ello

Monday, April 10, 2017

Docker, Jenkins, Github

Ensure that user belongs to docker group

[steve ~]$ id steve
uid=1000(steve) gid=1000(steve) groups=1000(steve),10(wheel),975(vboxusers),1002(docker)

User a persistent volume (to keep data), and the :z option to deal with selinux

[steve ~]$ docker run -p 8080:8080 -p 50000:50000 -v /home/steve/jenkins:/var/jenkins_home:z jenkins

Create repo on github, push code and makefile

https://github.com/bugwhine/hellojenkins

Install gcc and related tools into jenkins container

[steve ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                              NAMES
04105a89f166        jenkins             "/bin/tini -- /usr/lo"   5 minutes ago       Up 5 minutes        0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp   ecstatic_williams

[steve ~]$ docker exec -u root  04105a89f166 apt-get -y install gcc make

Create job in jenkins

- Source Code Management
  - git
  - Repository URL: git@github.com:bugwhine/hellojenkins.git
  - Credentials: bugwhine (add using dropdown)
- Build
  - execute shell
    - make

Build now

Saturday, December 31, 2016

Learning linux driver development with qemu


Linux Drivers

PCI

Based on instructions here -
http://nairobi-embedded.org/linux_pci_device_driver.html

Build yocto guest OS

$ git clone git://git.yoctoproject.org/poky
poky]$ git checkout morty
poky]$ source oe-init-build-env

#for those of us with non-US keyboards (you can also skip this step if using 'nographic' mode for qemu
build]$ echo "IMAGE_INSTALL_append = \" kbd keymaps kbd-keymaps\"" >> conf/local.conf

#this will take a while
build]$ bitbake core-image-sato-sdk

#now run it in qemu
build]$ runqemu qemux86-64 core-image-sato-sdk ext4 nographic qemuparams="-device ivshmem,shm=ivshmem,size=1"

#change keyboard layout if needed
root@qemux86-64:~# loadkeys /usr/share/keymaps/i386/qwerty/sv-latin1.map.gz 

#build guest module
#copy code to guest
[host pci]$ scp -r guest/ root@192.168.7.2:~/

ssh root@192.168.7.2
root@qemux86-64:~# cd /usr/src/kernel
root@qemux86-64:/usr/src/kernel# make modules_prepare

root@qemux86-64:/usr/src/kernel# cd ~/guest
root@qemux86-64:guest # make
root@qemux86-64:~/guest# sudo insmod ne_ivshmem_ldd_basic.ko
root@qemux86-64:~/guest# ls -l /dev/ivshmem0
root@qemux86-64:~/guest# sudo ./ne_ivshmem_shm_guest_usr -w "Dunia, vipi?"
main:169:: writing "Dunia, vipi?"

[host pci]$ hexdump -C /dev/shm/ivshmem
00000000  44 75 6e 69 61 2c 20 76  69 70 69 3f 00 00 00 00  |Dunia, vipi?....|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|


User mode
Now for user-mode, based on instructions here