jncronin / rpi-boot

A second stage bootloader for the Raspberry Pi
GNU General Public License v2.0
141 stars 48 forks source link

multiboot_info structure does not match grub #20

Open eryjus opened 7 years ago

eryjus commented 7 years ago

While not technically following the 0.96 version of multiboot, grub offers some additional (very useful) fields in the multiboot_info structure that would be very helpful if rpi-boot matched.

This additional information begins here: https://github.com/coreos/grub/blob/master/include/multiboot.h#L194 and is as follows:

  multiboot_uint64_t framebuffer_addr;
  multiboot_uint32_t framebuffer_pitch;
  multiboot_uint32_t framebuffer_width;
  multiboot_uint32_t framebuffer_height;
  multiboot_uint8_t framebuffer_bpp;
#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB     1
#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
  multiboot_uint8_t framebuffer_type;
  union
  {
    struct
    {
      multiboot_uint32_t framebuffer_palette_addr;
      multiboot_uint16_t framebuffer_palette_num_colors;
    };
    struct
    {
      multiboot_uint8_t framebuffer_red_field_position;
      multiboot_uint8_t framebuffer_red_mask_size;
      multiboot_uint8_t framebuffer_green_field_position;
      multiboot_uint8_t framebuffer_green_mask_size;
      multiboot_uint8_t framebuffer_blue_field_position;
      multiboot_uint8_t framebuffer_blue_mask_size;
    };
};

Please consider adding these additional fields to rpi-boot's multiboot_info structure and populate them as as appropriate.

jncronin commented 7 years ago

Apologies for the delay. I've added the fields as per the grub distribution's multiboot.h. I did it without a rpi to test on so the red and blue field positions may have been reversed and I'd appreciate if you could confirm they are in the right order.

eryjus commented 7 years ago

Thank you for adding the additional support!! No problem on the delay. It will take me a bit to drum up a test since I have not yet been working on real hardware. To be honest, it was the frame buffer address and dimensions I was more interested in; I do not use the other information in my kernel. However, I am committed to help you test. I do have an rpi2b I can test on and I will work on that immediately.

I will keep you posted on my progress.

Adam

eryjus commented 7 years ago

After some quick research on how the fields are populated and how I should expected to be able to test them, I have been able to understand the following.

A simple code review leads me to believe that your bits are in the right order. However, you only support 24-bit (or RBG888) color encoding, regardless of the actual settings requested. I did find the following in grub2's grub/include/video.h which should help make sense of the encoding:

#define GRUB_VIDEO_MI_RGB555(x)                     \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,               \
    x.bpp = 15,                             \
    x.bytes_per_pixel = 2,                      \
    x.number_of_colors = 256,                       \
    x.red_mask_size = 5,                        \
    x.red_field_pos = 10,                       \
    x.green_mask_size = 5,                      \
    x.green_field_pos = 5,                      \
    x.blue_mask_size = 5,                       \
    x.blue_field_pos = 0

#define GRUB_VIDEO_MI_RGB565(x)                     \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,               \
    x.bpp = 16,                             \
    x.bytes_per_pixel = 2,                      \
    x.number_of_colors = 256,                       \
    x.red_mask_size = 5,                        \
    x.red_field_pos = 11,                       \
    x.green_mask_size = 6,                      \
    x.green_field_pos = 5,                      \
    x.blue_mask_size = 5,                       \
    x.blue_field_pos = 0

#define GRUB_VIDEO_MI_RGB888(x) \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,           \
    x.bpp = 24,                         \
    x.bytes_per_pixel = 3,                  \
    x.number_of_colors = 256,                   \
    x.red_mask_size = 8,                    \
    x.red_field_pos = 16,                   \
    x.green_mask_size = 8,                  \
    x.green_field_pos = 8,                  \
    x.blue_mask_size = 8,                   \
    x.blue_field_pos = 0

#define GRUB_VIDEO_MI_RGBA8888(x) \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,   \
    x.bpp = 32,                 \
    x.bytes_per_pixel = 4,          \
    x.number_of_colors = 256,           \
    x.reserved_mask_size = 8,           \
    x.reserved_field_pos = 24,          \
    x.red_mask_size = 8,            \
    x.red_field_pos = 16,           \
    x.green_mask_size = 8,          \
    x.green_field_pos = 8,          \
    x.blue_mask_size = 8,           \
    x.blue_field_pos = 0

#define GRUB_VIDEO_MI_BGR555(x)                     \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,               \
    x.bpp = 15,                             \
    x.bytes_per_pixel = 2,                      \
    x.number_of_colors = 256,                       \
    x.red_mask_size = 5,                        \
    x.red_field_pos = 0,                        \
    x.green_mask_size = 5,                      \
    x.green_field_pos = 5,                      \
    x.blue_mask_size = 5,                       \
    x.blue_field_pos = 10

#define GRUB_VIDEO_MI_BGR565(x)                     \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,               \
    x.bpp = 16,                             \
    x.bytes_per_pixel = 2,                      \
    x.number_of_colors = 256,                       \
    x.red_mask_size = 5,                        \
    x.red_field_pos = 0,                        \
    x.green_mask_size = 6,                      \
    x.green_field_pos = 5,                      \
    x.blue_mask_size = 5,                       \
    x.blue_field_pos = 11

#define GRUB_VIDEO_MI_BGR888(x) \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,           \
    x.bpp = 24,                         \
    x.bytes_per_pixel = 3,                  \
    x.number_of_colors = 256,                   \
    x.red_mask_size = 8,                    \
    x.red_field_pos = 0,                    \
    x.green_mask_size = 8,                  \
    x.green_field_pos = 8,                  \
    x.blue_mask_size = 8,                   \
    x.blue_field_pos = 16

#define GRUB_VIDEO_MI_BGRA8888(x) \
  x.mode_type = GRUB_VIDEO_MODE_TYPE_RGB,   \
    x.bpp = 32,                 \
    x.bytes_per_pixel = 4,          \
    x.number_of_colors = 256,           \
    x.reserved_mask_size = 8,           \
    x.reserved_field_pos = 24,          \
    x.red_mask_size = 8,            \
    x.red_field_pos = 0,            \
    x.green_mask_size = 8,          \
    x.green_field_pos = 8,          \
    x.blue_mask_size = 8,           \
    x.blue_field_pos = 16

Now, a quick test with qemu shows that, at least emulated, red in in the most significant bits. With this information, the test should be rather quick on real hardware.

Oh, I am using RGB565 for my kernel initialization. See: https://github.com/eryjus/century/blob/master/modules/loader/src/rpi2b/framebuffer.c#L47. The hope is to be able to move this code to allow all the stage 2 loaders to handle the video init.

eryjus commented 7 years ago

OK, I promise I have not forgotten my commitment. I wanted to stop and take a moment to provide an update. In short, I had not tried to boot on real hardware until this test and I'm having a miserable time getting it to boot. It took some time to determine if the kernel.img was getting control or not and I finally had to resort to blinking-light debugging.

First, my hardware -- I am testing on a Raspberry Pi 2B. I know that this is the BCM2835 SoC, and I have been able to do other sample tests (inspired by these: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/) in assembly and using the fixed addresses for the rpi2b in the 0x3f000000 range. I added code to blink the light here: https://github.com/jncronin/rpi-boot/blob/master/boot.s#L36, which included hard-coded MMIO addresses, and confirmed that I am booting and the kernel is getting control.

With a lot of trial and error (and an line by line exhaustive search), inserted a function call to my blinking light code and was able to determine that this line was where the kernel locked up: https://github.com/jncronin/rpi-boot/blob/master/main.c#L210. When I changed the addresses in uart_init() to 0x3f...., I was able to get past the uart_init() function and it died later.

So, I added a define to turn on DEBUG, and the code did not make is as far. In fact, I was able to trace the lock-up point to this line: https://github.com/jncronin/rpi-boot/blob/master/main.c#L186. In short, printf() is being called ahead of the initialization of base_adjust. At least that is my conclusion.

Now, as an aside, I found this list of ARM Machine Types and found the BCM2835 on there as typ 4828 (or 0x12dc): http://www.arm.linux.org.uk/developer/machines/. When I add that machine type into this line (https://github.com/jncronin/rpi-boot/blob/master/main.c#L208) to set base_adjust to 0x1f... , the kernel locks again.

Well, long story short, I'm missing something. I believe there is an initialization sequence issue that QEMU is tolerant to (which I conclude since I have done all my testing on QEMU so far and you do not have rpi hardware to test on....) that the real hardware is not.

Do you have any thoughts on what this might be? Also, any debugging tips other than blinking the light to check flow and value equality?

jncronin commented 7 years ago

Thanks for the update. I've fixed the out-of-order printf() statement.

I'm afraid I don't have an rpi2 for testing. Do you know which firmware is installed on your SD card? The older ones use atags for memory locations and fixed UART/FB register offsets whereas the newer ones use DeviceTree to specify these addresses. I'd advise using the newer firmware.

For a quick debugging of DeviceTree addresses, I'd advise something like:

uart_set_base(...); gpio_set_base(...); base_adjust = 0;

at the beginning of kernel_main (just after output_enable_uart() for example) using appropriate addresses known to work for the rpi2.

Then, in all the _cb(const char dtb, int node, int parent) functions in rpifdt.c comment out the last line (second call to parse_reg).

This will then dump the device tree to the uart but not actually use the values to change anything.

eryjus commented 7 years ago

Thank you for getting back to me. I have been quite busy the last couple of weeks and I have not been able to spend as much time on this as I would like. I just wanted to check in with you. I do not expect things to get much better over the next few weeks.

I did have to acquire a cable for UART logging which I did not have. It is on the way.

In the meantime, I have been able to use my blinking light debugging technique to trace the failure into the first call to malloc(), which is used to allocate memory to store the system memory blocks. I see that malloc() is supposed to be self-initializing, and it is looking for memory past _end to allocate. It fails somewhere in that initialization block.

More as I find it.

eryjus commented 7 years ago

Hi,

I wanted to provide an update. Things have been rough-going for the last few weeks -- and my personal free time is not much these days.

I have made some changes in order to test with the equipment I have. I procured a serial-USB cable. The cable I purchased is a 4-wire (of which I use 3 - TX. RX. GND); therefore, there is no hardware flow control. In particular, I could only get 20 characters of data printed. I chose to rewrite the uart.c file to use the AUX mini uart. This helped a lot.

I also stripped down the boot image. I only have the following 3 files on my SD drive:

  1. bootcode.bin
  2. start.elf
  3. kernel7.img

I do not have any of the .dtb files on the SD drive. Therefore, what I am working with at the moment are ATAGS.

With all of this, I am finally in a position to narrow down where the failure is. I am continuing to work on it; I have not given up.

eryjus commented 5 years ago

To close the loop on this, I believe that this can be closed.

As I have progressed in my own project, I have learned that qemu does not emulate the rpi2b 100%. One of the challenges is that qemu emulates the UART but not the mini-UART while I have yet to be able to find the trick to make the PL011 UART work properly on real hardware.

If you would still like a real hardware test, I understand. I will write something purpose-built to do this.