Uboot

1 Tổng quan

Khi cấp nguồn 1 PC lên, trước khi hệ điều hành chiếm quyền điều khiển phần cứng thì phải có 1 phần mềm khác đã được chạy bởi phần cứng. Phần mềm được chạy đầu tiên đó chính là bootloader. Nhiệm vụ của bootloader chính là load hệ điều hành vào hệ thống.

 Bootloader có 2 được chia ra thành 2 loại chính:

  • Uboot: thiên về open source, miễn phí, phù hợp cho các kiến trúc arm – kiến trúc thiên về cho các thiết bị embedded nhỏ gọn
  • Grub: cho kiến trúc x86

2. Quá trình khởi động của thiết bị embedded

Trước khi tìm hiểu về uboot, chúng ta cần tìm hiểu về quá trình khởi động của 1 thiết bị embedded. Quá trình này được chia ra làm 3 giai đoạn:

  • First stage boot loader
  • Second stage boot loader
  • Third stage boot loader

First stage boot loader

Là chương trình được lưu trong vùng nhớ Rom của SoC, nằm tại địa chỉ 0x20000 (fuse ROM – lưu trữ vĩnh viễn và không thay đổi được)

Ngay sau khi power on, CPU sẽ tìm tới địa chỉ 0x20000 và thực thi chương trình trên – còn gọi là chương trình boot ROM.

Chương trình boot ROM có nhiệm vụ: Khởi tạo 1 số clock (cho RAM, chân Pin cho SD card), pin muxing, disable watchdog… và đọc giá trị của SYSBOOT_PIN để load second stage boot loader.

Ngoài ra, chương trình Boot Rom khởi tạo ra Booting device list, nơi chứa mức độ ưu tiên cho việc sử dụng thiết bị để booting cho con chip

Xét chương trình boot ROM trên beaglebone black:

  • Set up the booting device list: beaglebone black cho phép uboot được đọc từ 5 device khác nhau (MMC, SD card, SPI, Serial, USB). Vì vậy, cần có sự phân cấp mức độ ưu tiên boot từ các device được thực hiện trong bước Set up the booting device list

Beaglebone black có hệ thống SYSBOOT_PIN bao gồm 15 chân pin. Giá trị điện áp tại mỗi chân Pin sẽ quyết định 1 thuộc tính khi boot (Crystal Frequency, Pinmuxing, Boot sequence).

Trong đó 5 chân pin cuối cùng dùng để tạo mức độ ưu tiên cho các booting device.

  • Sau đó, boot ROM tiến hành đọc dữ liệu ở thiết bị có mức độ ưu tiên cao nhất và check lỗi của dữ liệu đó. Nếu chương trình boot từ thiết bị không hợp lệ, nó sẽ thực hiện boot từ các thiết bị có mức độ ưu tiên thấp hơn liền kề.

Trên thực tế, để tránh việc bị thay đổi chương trình gốc của thiết bị, các hãng sản xuất đã dựa vào tính năng Secure boot để đảm bảo tính bảo mật của sản phẩm.

  • ROM code lưu public key và private key
  • Image lưu trên thiết bị nhớ dùng để boot cho con chip phải được mã hoá bằng public key.
  • Sau khi load image và giải mã trên ram, chương trình ROM sẽ check tính hợp lệ của image – cho phép boot.

Do đặc thù kích thước nhỏ và không thể ghi đè lại. Nên chương trình boot ROM thường sẽ làm rất đơn giản. Tuy nhiên ngày nay, có nhu cầu về những cách thức boot phức tạp hơn. Ví dụ như cho phép boot OS từ ethernet, serial, hiển thị logo, boot song song nhiều OS…

Vì vậy cần phải có 1 chương trình boot loader phức tạp hơn. Chương trình sẽ được lưu ở bộ nhớ ngoài với kích thước lớn hơn. Uboot ra đời.

Second và third stage boot loader

Soucre code uboot được build xong sẽ được 2 file: SPL và TPL

Uboot sẽ đóng vai trò là second stage (SPL) và third stage (TPL) boot loader. Ví dụ uboot trong beaglebone black sẽ tướn ứng với 2 file: MLO – SPL và u-boot.img – TPL

SPL khởi tạo phần cứng và load tiếp TPL từ thiết bị boot vào chip.

TPL bao gồm các driver, thư viện, command. TPL thực hiện boot script là 1 tập hợp các command line làm nhiệm vụ đọc kernel image từ thiết bị boot, nạp nó vào RAM và giải nén. Sau đó, TPL chạy hàm start_kernel dựa vào địa chỉ trong kernel header của file image – hàm này tiến hành load ramdisk và run init process trong, init process sẽ tiến hành load tiếp các process khác.

Những tình huống trong dự án mà chúng ta phải làm việc với uboot:

  • Check phần cứng. Một số lỗi xảy ra mà chúng ta không thể phân biệt được đó là lỗi của phần cứng hay phần mềm.
  • Cấu hình lại chân pin: Board BBB đang có mặc định 4 cổng serial. Chúng ta muốn lấy chân pin của 3 cổng để dùng vào việc khác => Cấu hình lại chân pin.
  • Sửa lại giao diện boot: Một số thiết bị cho phép hiển thị logo ngay khi bật nguồn.

Nhìn chung, đa số công việc mà chúng ta làm với uboot sẽ làm nhiệm vụ chuẩn bị môi trường để boot được linux từ thiết bị lưu trữ.

Kiến trúc của uboot khá tương đồng với kiến trúc của Linux

Uboot được xây dựng với tư tưởng như sau:

  • Tầng thấp nhất là các device driver. Chúng làm nhiệm vụ điều khiển phần cứng. Chức năng sẽ tập chung vào cấu hình clock, power, pinmuxing, đọc ghi dữ liệu từ thiết bị nhớ.
  • Tầng thư viện: Là các library với những API như printf, debug_log, read(), write()…
  • Tầng application: Là các command line. Như read, load, bootm…

Mục đích cuối cùng của uboot là thực hiện boot script (tương tự với script trên linux). Boot script sẽ gọi 1 loạt các command line để thực hiện việc boot hệ điều hành: đọc kernel từ thiết bị boot, nạp vào RAM và giải nén,..

Uboot source code

  • Device tree.
    • Syntax về device tree trong uboot follow theo đúng với Linux.
    • Device tree trong uboot được đính kèm trong file uboot.img chứ không được build riêng ra file dtb
  • Device driver
    • Template về driver của uboot khá giống với Linux.
  • Command line

Xét ví dụ vào giao diện của uboot trên BBB, khi thực hiện boot bằng thẻ nhớ và nhấn đồng thời nút S2 khi cấp nguồn. Sau đos, ta phải thực hiện nhấn Space ngay lập tức để có thể vào được giao diện của Uboot và tương tác với uboot qua các command line như sau:

Từ đây, bằng cmd “md” ta có thể đọc được memory từ một địa chỉ cụ thể

Ví dụ: Đổi thời gian chờ nhấn Space để dừng ở giao diện command line của uboot

Khi uboot khởi động, muốn dừng ở giao diện đó thì phải nhấn Spacee trong vòng 3s. Bây giờ, ví dụ này sẽ thay đổi thời gian trên thành 30s bằng cách sửa config build uboot và build lại uboot rồi nạp vào BBB.

Các bước sửa code như sau:

  1. Dùng cmd:

          để tìm nơi define dòng log trên trong file config uboot cho BBB:

Ta thấy nó được define bởi CONFIG_AUTOBOOT_PROMPT trong configs/am335x_evm_defconfig

2. Tiếp tục tìm kiếm define AUTOBOOT_PROMPT ta thấy nó xuất hiện trong file Kconfig

3. Đi vào file Kconfig ta thấy thời gian được hiện thị thông qua CONFIG_BOOTDELAY

Bây giờ ta thực hiện việc sửa đổi CONFIG_BOOTDELAY trong file am335x_evm_defconfig thành 30

Cuối cùng ta thực hiện build lại uboot và nạp vào BBB và sẽ thấy dòng log cho phép chờ nhấn Space đã được thay đổi

3. Ví dụ bật tắt LED trong uboot

Để làm được ví dụ trên, cần:

  • Viết 1 driver điều khiển đèn led
  • Viết thêm 1 command line để bật tắt led thông qua driver

Ví dụ điều khiển đèn led tại P1:14 theo luồng hoạt động sau:

Muốn thêm 1 command line trong uboot, ta cần khởi tạo file code C “demo_led.c” cho command line đó trong thư mục u-boot/cmd/

File code C cho command line tuân theo template trong uboot bằng cách khai báo macro U_BOOT_CMD

Với

  • Tham số đầu tiên “demo_led” là tên của command line mình muốn thêm vào
  • Tham số thứ hai “4” là số argument thêm vào cho command line
  • Tham số thứ ba “1” là repeat (với 1 thì người dùng sau khi gõ câu lệnh chỉ cần nhấn Enter 1 lần nữa là sẽ thực hiện command line mà không cần gõ lại)
  •  Tham số thứ tư “do_demo_led” là con trỏ hàm trỏ vào hàm thực thi của command line đó
  • Cuối cùng là các chuỗi kí tự gợi ý cho command line

Hàm “do_demo_led” 4 tham số truyền vào theo chuẩn của uboot. Trong đó argument truyền vào command line lưu trong mảng argv[]. Hàm này thực hiện phân tích argument truyền vào và thực hiện điều khiển led theo các yêu cầu của người dùng.

Port file code cho cmd “demo_led.c” vào uboot bằng cách sửa file Kconfig và Makefile trong thư mục /u-boot/cmd:

Kconfig:

Makefile:

Để file “demo_led.c” có thể chạy được trong uboot, ta cần phải thêm một số config vào configs/am335x_evm_defconfig để link source code vào soucre của uboot

Tạo thư mục chứa driver cho led trong thư mục /drivers của uboot: /u-boot/drivers/demo_led . Đầu tiên ta thêm vào Makefile trong thư mục /u-boot/drivers dòng sau:

và chỉ định file source của driver trong /u-boot/drivers/Kconfig:

Trong  thư mục /u-boot/drivers/demo_led  tạo Makefile và Kconfig để compile driver cho led

Makefile:

Kconfig:

Source file của driver “demo_led.c”:

Cuối cùng ta thực hiện thêm vào Device Tree node cho led mà ta muốn sử dụng (ở đây ta ví dụ led ở chân P1.14)

/u-boot/arch/arm/dts/am335x-boneblack.dts:

cấu hình pinmuxing cho chân pin P1.14 với chức năng GPIO

Sau khi đã hoàn thành các bước trên, thực hiện build lại uboot và nạp vào board, ta thực hiện được các command line như đã thêm vào:

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top