1 điểm bởi GN⁺ 2024-04-07 | 1 bình luận | Chia sẻ qua WhatsApp
  • UEFIRC là một trình khách IRC đồ họa chạy trong môi trường tiền khởi động UEFI của firmware bo mạch chủ trước khi hệ điều hành khởi chạy, cho thấy ngay cả trong môi trường dành cho bootloader vẫn có thể triển khai giao diện và tính năng mạng gần giống một ứng dụng thông thường
  • Việc triển khai tận dụng driver NIC và ngăn xếp TCP mà UEFI cung cấp cho khởi động qua mạng, và backend mạng vmnet cho QEMU giúp việc phát triển trở nên khả thi
  • Điểm khó nhất là xử lý giao thức TCP của UEFI trong Rust, nơi trạng thái toàn cục, callback tái nhập, buffer scatter-gather, cùng event, token, handle và protocol đan xen phức tạp
  • GUI là phiên bản được mang sang UEFI của bộ công cụ GUI Rust và trình kết xuất TrueType của axle, đồng thời libgui cũng được cải tiến để hỗ trợ nhập chuột, thanh cuộn và kết xuất văn bản trong khung nhìn cuộn
  • Kết quả cuối cùng gần với một dự án đùa công phu hơn là một trình khách IRC thực dụng, nhưng nó vẫn là công cụ để than phiền về ngăn xếp TCP/IP của UEFI ngay từ bên trong UEFI qua IRC

UEFIRC làm gì

  • UEFIRCmột trình khách IRC đồ họa chạy trên UEFI
  • Nó được viết bằng Rust và sử dụng bộ công cụ GUI cùng trình kết xuất TrueType được tạo cho không gian người dùng của axle
  • Có thể kết nối tới máy chủ IRC, trò chuyện và đọc tin nhắn
  • Trong quá trình phát triển, backend mạng vmnet cho QEMU đã được sử dụng

UEFI như một sân khấu thực thi

  • Bootloader của hệ điều hành được nạp với sự trợ giúp của firmware lưu trong ROM của bo mạch chủ
  • BIOS trước đây có nhiều hạn chế, và tiêu chuẩn UEFI được tạo ra để thay thế
    • BIOS yêu cầu bootloader khởi đầu ở chế độ 16-bit
    • Cũng có yêu cầu bộ nạp giai đoạn đầu phải nằm trong 512 byte
  • UEFI đặt bootloader vào môi trường 64-bit ngay từ đầu và cung cấp API cho những việc như chuyển độ phân giải hiển thị VESA, cấp phát bộ nhớ và truy cập hệ thống tệp EFI
  • Đây là một bước tiến lớn so với BIOS, nhưng cũng bị đánh giá là thiết kế quá mức

Tái sử dụng tính năng khởi động qua mạng cho IRC

  • Một số bootloader có thể nạp hệ điều hành qua mạng thay vì từ thiết bị khối cục bộ
  • Để hỗ trợ trường hợp sử dụng này, firmware UEFI phải bao gồm ngăn xếp mạng
    • Driver NIC
    • Triển khai TCP
    • API để các ứng dụng chạy trong môi trường tiền khởi động truy cập ngăn xếp này
  • Vì bootloader không nhất thiết chỉ để nạp hệ điều hành, nên trong cùng môi trường đó cũng có thể chạy một trình khách IRC

Khó khăn khi xử lý UEFI TCP trong Rust

  • Phần khó nhất của dự án là triển khai trình khách giao thức TCP của UEFI bằng Rust
  • Giao thức TCP của UEFI đòi hỏi các vòng đời dữ liệu và tương tác rất khó biểu đạt trong Rust
    • Trạng thái toàn cục
    • Callback tái nhập
    • Buffer scatter-gather
    • Event, token, handle, protocol
  • Mã Rust đã được kiểm thử nhiều ngày để loại bỏ rò rỉ bộ nhớ và lỗi use-after-free trong buffer nhận TCP

Sự rối rắm của NOTIFY_SIGNALNOTIFY_WAIT

  • Chỉ nhìn tên thì rất khó đoán được cách hoạt động của API event trong UEFI
  • Nếu chỉ định NOTIFY_SIGNAL, callback sẽ được gọi khi event xảy ra, và việc dùng wait() sẽ là lỗi
  • Nếu chỉ định NOTIFY_WAIT rồi gọi wait(), UEFI có thể gọi callback nhiều lần trước khi event xảy ra, và khi event xảy ra thì wait() mới được giải phóng
  • Hai chế độ này tạo ra ý nghĩa hoàn toàn khác nhau ngay cả với cùng một callback
    • NOTIFY_SIGNAL: event đã xảy ra, đây là lúc làm bước tiếp theo
    • NOTIFY_WAIT: event vẫn chưa xảy ra, đây là lúc thúc đẩy tiến trình tiếp tục
  • Cuối cùng, để đệm bất đồng bộ dữ liệu gói nhận được, tác giả dùng vòng lặp NOTIFY_WAIT cùng một bộ hẹn giờ timeout ngắn

Hỗ trợ chuột và con trỏ

  • Chuột không phải là thứ bắt buộc với trình khách IRC, nhưng nó khiến ứng dụng có cảm giác tương tác hơn
  • Dùng Simple Pointer Protocol của UEFI để đọc chuyển động chuột và đầu vào nút bấm, rồi đưa phản hồi vị trí con trỏ vào GUI
  • Simple Pointer Protocol không hỗ trợ bánh xe cuộn
    • Trong UEFIRC, phải dùng phím mũi tên hoặc kéo thanh cuộn bằng con trỏ
  • Vì firmware UEFI OVMF tiêu chuẩn không cung cấp được event chuột, tác giả đã build firmware UEFI tùy biến có kèm các driver và protocol cần thiết như UsbMouseDxe
  • Để có thể thử UEFIRC trong QEMU, firmware UEFI đó cũng được đăng lên phần phát hành

Tỉ lệ hóa chuyển động chuột

  • Driver chuột báo cáo độ thay đổi vị trí chứ không phải vị trí tuyệt đối
  • Việc chỉ cộng tuyến tính delta_x, delta_y như nguyên bản tạo cảm giác chậm chạp
  • Các hệ điều hành dùng cách tỉ lệ hóa gần hơn với việc vừa cho phép di chuyển nhanh vừa điều chỉnh tinh
  • Trong cách triển khai ví dụ, độ lớn di chuyển con trỏ được nhân với giá trị áp dụng log2() lên tổng trị tuyệt đối của lượng dịch chuyển
  • Con trỏ di chuyển tuyến tính dễ khiến toàn bộ môi trường có cảm giác chậm và phản hồi kém

Mô hình hóa tin nhắn IRC

  • Việc mô hình hóa tin nhắn IRC tương đối đơn giản và dễ chịu
  • IRC dùng định dạng dòng dựa trên văn bản, nên khá dễ parse
  • Tuy nhiên, do nhiều thập kỷ mở rộng, vẫn tồn tại gánh nặng là chỉ một phần được tiêu chuẩn hóa

Dùng libgui trong UEFI

  • Bộ công cụ GUI Rust của axle trước đó đã được chuẩn bị khá nhiều để có thể dùng ngoài ngữ cảnh axle, nên việc chạy nó trong UEFI không quá khó
  • Công việc cốt lõi là cung cấp một triển khai AwmWindow có thể dùng bên trong UEFI
  • Sau đó có thể tận dụng nguyên vẹn nhiều tính năng của libgui
    • Quản lý event
    • Kết xuất phông chữ
    • Ghép lớp
    • Trang trí khung nhìn
    • Các thành phần phức tạp như khung nhìn cuộn

Thanh cuộn và kết xuất văn bản trong khung nhìn cuộn

  • libgui viết bằng C của axle đã có tính năng thanh cuộn, nhưng bản Rust vẫn còn thiếu một số phần
  • Vì phần tương tác chính của UEFIRC diễn ra trong một khung nhìn cuộn đầy văn bản, nên tính năng thanh cuộn đã được triển khai lại trong libgui Rust
  • Khung nhìn cuộn có chi phí kết xuất pixel lớn hơn khung nhìn kích thước cố định
    • Với khung nhìn cố định, chỉ cần nghĩ tới buffer RGB kích thước width * height
    • Khung nhìn cuộn phải xử lý một canvas có thể mở rộng vô hạn
  • Bộ công cụ GUI Rust của axle xử lý khung nhìn cuộn theo kiểu dựa trên tile
    • Mỗi tile là một buffer pixel hình vuông rộng vài trăm pixel
    • Chỉ cấp phát các tile cần thiết cho vùng mà nội dung thực sự được kết xuất
    • Tính toán các tile hiển thị được rồi ghép chúng thành hình ảnh cuối cùng
  • Nếu trình kết xuất TrueType gọi putpixel() cho từng pixel của glyph, khung nhìn cuộn sẽ không biết trước toàn bộ vùng cần kết xuất, dẫn tới kém hiệu quả
  • Để giải quyết, polygon stack được bổ sung vào các đơn vị vẽ cơ bản như đường thẳng, hình tròn và hình chữ nhật
    • Khung nhìn cuộn có thể biết trước rằng một polygon lớn sắp được vẽ và cấp phát sẵn các tile cần thiết
    • Tác giả không thật sự thích việc dùng tô đa giác tùy ý như primitive cơ bản, nhưng trên thực tế nó hoạt động tốt

libgui được cải thiện trong quá trình làm UEFIRC

Kết quả hoàn toàn không cần thiết

  • Bản thân trình khách IRC là một dự án đùa công phu nên không thực sự hữu ích trong sử dụng thực tế
  • Nhưng khi bực mình với ngăn xếp TCP/IP của UEFI, nó có thể dùng như một công cụ để than phiền về điều đó
  • Cuối cùng, tác giả đã vào kênh IRC phát triển UEFI #edk2 từ bên trong UEFI và để lại lời chào

1 bình luận

 
GN⁺ 2024-04-07
Ý kiến trên Hacker News
  • Tác giả làm một client IRC đồ họa chạy chỉ trong môi trường trước khi khởi động UEFI cho vui, và còn nhét vào những tính năng quá đà như phông chữ TrueType, con trỏ, trang trí GUI
    Ban đầu đây là dự án để làm thứ gì đó nhanh và nhẹ sau khi đã mệt với việc tự viết bộ thu GPS từ đầu, nhưng như mọi khi, nó mất nhiều thời gian hơn dự kiến rất nhiều
    Trong bài, tác giả cũng dành khá nhiều thời gian cho phần trực quan hóa cách mô hình hóa scroll view và render vào viewport tĩnh; hy vọng mọi người xem thấy thú vị
    Lúc đầu, với ý tưởng “cố nhét thứ không nên có vào UEFI”, tác giả nghĩ đến một client Twitter, nhưng đã có người làm khá tốt bằng giao thức HTTP của UEFI rồi nên quyết định tránh HTTP
    Vì vậy tác giả chọn IRC, thứ chạy trên TCP và cũng mang cảm giác mạng xã hội hoàn toàn không hợp với môi trường trước khi khởi động

    • Tuy nói là “có cảm giác không nên ở gần môi trường trước khi khởi động”, nhưng nếu muốn nhờ trợ giúp về sự cố boot thì đây lại có vẻ là đúng chỗ
    • Muốn bỏ hệ điều hành cồng kềnh vô ích cùng đủ thứ tính năng lặt vặt để chuyển sang UEFI nhỏ gọn và đơn giản hơn. Như vậy khởi động cũng nhanh hơn và phát triển “nhúng” cũng dễ hơn
      Tất nhiên là nói đùa. Ít nhiều là vậy
      Là người theo chủ nghĩa tối giản nên tôi không cần GUI hay chuột, và UEFI dường như cũng đã nhiều hơn mức tôi cần
      Client Twitter được nhắc tới ở đây: https://github.com/arata-nvm/mitnal
    • Nếu một phần mềm quá lớn để nhét vào UEFI thì ngay từ đầu nên coi tất cả nó là phần mềm phình to không cần thiết. Ngày xưa chỉ cần hai đĩa mềm 360KB là đủ
    • Thật sự rất hay. Từ lâu tôi đã tò mò liệu có thể lưu thông tin xác thực VPN trong UEFI, rồi để hệ thống kết nối tới máy chủ và boot mạng PXE hay không
      Trông có vẻ có thể là một cách khá ổn, và có lẽ an toàn, để tự động khôi phục các hệ thống từ xa khi bản cài đặt hỏng hoàn toàn và không thể boot bình thường
    • Tôi muốn nghe thêm về phần bộ thu GPS tự viết từ đầu
  • Rất hay. Nó cũng cho thấy rằng bên dưới hệ thống mà hầu hết mọi người nghĩ tới có một lớp phần mềm phức tạp và mạnh mẽ hơn ta tưởng
    Người ta thường hiểu nhầm rằng hệ điều hành là “tầng thấp nhất” của stack phần mềm, nhưng thực ra còn có mã dạng firmware mới là thứ thật sự sở hữu hệ thống
    Đôi khi nó làm xong việc rồi biến mất, đôi khi nó ở lại trong suốt thời gian hệ thống bật, tới mức ngay cả hệ điều hành cũng cảm thấy nó trong suốt
    Có thái độ cho rằng “nó chỉ là mã cấp thấp để điều khiển thiết bị, ở đó chẳng có chuyện gì nghiêm trọng xảy ra”, nhưng nếu bên dưới đó còn nhét được cả client IRC thì hoàn toàn có thể tưởng tượng ra những việc ác ý khác

  • “Tại sao?” ư, rốt cuộc “tại sao” là câu hỏi kiểu gì vậy? Tôi đến HN để thấy tinh thần này
    “Một nhận thức đáng sợ nhất ập đến với tôi. Việc tôi làm chẳng có lý do nào cả. Tôi biết vì sao mình làm. Tôi làm vì nghĩ nó sẽ vui. Nhưng họ sẽ hỏi ‘rốt cuộc tại sao anh lại làm chuyện này’, và nếu tôi không có một lý do đủ nghe được thì có lẽ họ sẽ tống tôi vào bệnh viện tâm thần.” — Boyd Rice

  • Không cần tự đánh giá thấp mình. Ở đây có một dự án client chỉ huy và kiểm soát botnet
    UI hơi buồn cười

  • Thật sự rất tuyệt. Tôi không biết UEFI API lại dễ tiếp cận và được tài liệu hóa tốt như vậy
    Tôi tò mò vòng lặp phát triển diễn ra thế nào. Chắc là chạy trong VM, nhưng mỗi lần chạy client có phải “boot” lại không

    • Vòng lặp làm việc thường là boot một instance QEMU có nạp ứng dụng UEFI
      Script chạy chính sẽ tạo lại hệ thống tệp EFI chứa bản build mới của UEFIRC, rồi chuyển nó cho QEMU
      Tuy nhiên khi làm GUI, phần overhead này trở nên khá phiền, nên tác giả đã cấu hình để app có thể build cho cả UEFI thuần lẫn môi trường host chạy trên Mac
      Khi đổi build flag, GUI toolkit sẽ vẽ trực tiếp lên framebuffer do UEFI cung cấp, hoặc kết nối với hệ thống cửa sổ của Mac để gửi nhận sự kiện
      Overhead của cách tiếp cận hai target này cũng thấy được ở entry point: https://github.com/codyd51/uefirc/blob/main/src/main.rs
      Phần phân tích cú pháp tin nhắn IRC không cần trang trí gì thêm nên được phát triển bằng một bộ unit test chạy trực tiếp trên Mac, một số nằm ở đây: https://github.com/codyd51/uefirc/blob/main/src/irc/response...
    • QEMU có thể chạy ứng dụng UEFI
  • Một ngày nào đó tôi muốn hoàn tất việc viết hệ điều hành cho con bot IRC của mình, vốn vẫn còn đang chạy
    Có lẽ đây là điều vô dụng nhất để nói, nhưng di chuyển chuột phi tuyến tính, tức tăng tốc chuột, là thiết lập đầu tiên tôi tắt khi boot một hệ điều hành mới. Kỳ lạ là nó thực sự làm tay tôi đau
    Ví dụ trên Mac có linearmouse miễn phí, còn trên Windows thì chỉ cần tắt tăng tốc. Trên Linux thì dĩ nhiên là dễ
    Khi dùng tăng tốc chuột, rất khó học cảm giác ánh xạ giữa quãng đường chuột di chuyển và quãng đường trên màn hình; về lâu dài tôi cho rằng dùng không tăng tốc hiệu quả hơn
    Tôi học cách này từ game thủ, và tôi nghĩ game thủ vẫn có lý do chính đáng để làm vậy

    • Tôi không đổi thiết lập chuột nên không biết mặc định là gì, nhưng vẫn có thể bấm chính xác cả những vùng màn hình bị che
      Dù là cách nào thì cảm giác rồi cũng quen thôi. Giống như bàn đạp ga của ô tô thường cũng không ánh xạ trực tiếp với tốc độ
  • Nếu hỏi “tại sao?”, thì vì khi UEFI lần đầu được giới thiệu, những ứng dụng cấp thấp kiểu này từng được hứa hẹn
    Bên tạo ra UEFI thậm chí từng mơ sẽ thay thế cả những hệ điều hành mini chỉ để vào internet dựa trên Linux mà một số nhà cung cấp cho truy cập bằng cách nhấn phím nhất định trong lúc boot. Tôi không nhớ tên

    • Đó là tính năng Quick View / Quick Boot từng có ở những hãng như Dell ngày xưa. Thường nó boot thẳng vào vài ứng dụng năng suất
      Tôi đã xem một video trên YouTube nói khá sâu về nó; tôi nhớ ban đầu đó là một bản Linux rút gọn hoặc hệ điều hành tùy biến khác, sau đó chuyển sang ứng dụng UEFI, rồi cuối cùng hết thịnh hành
  • Bài viết hay. Nó làm tôi nhớ tới trò đùa Cá tháng Tư của bootloader barebox 2 năm trước. Tính năng là nếu mọi mục tiêu boot khác đều thất bại thì sẽ kết nối tới #barebox[1]
    Trọng tâm bên đó là thêm hỗ trợ TCP vào barebox, chứ không có các thành phần GUI đẹp như ở đây
    Giao diện chỉ là dòng lệnh, và nếu build barebox làm EFI payload thì có thể vẽ lên EFI GOP
    [1]: https://lore.barebox.org/barebox/20220401145902.GF4351@telli...

  • Tôi lập tức nhớ đến video gần đây của Cathode Ray Dude. Video nói về QuickLook, “client email” của HP, thực ra là plugin Outlook, và đây cũng là một sản phẩm đã được triển khai rồi bán ra theo kiểu này: https://www.youtube.com/watch?v=ssob-7sGVWs
    Video còn có những việc kỳ lạ hơn mà HP từng làm. Tuy nhiên dự án này làm được cả phần khó mà QuickLook đã tránh, đó là networking

  • Phần trực quan hóa trong bài viết hay và ấn tượng đến bất ngờ