booting from a disk/cd/usb - c

How can I boot my small console from a disk/cd/usb, with the following configuration:
The media that I want to use will be completely raw i.e no filesystem on it.
When I insert the media in my system or assume that its already inserted, I want to make it boot my own small OS.
The procedure that I want to use is that when my system starts, it boots from the disk/cd/usb, and starts my OS. For now imagine that the OS is going to be a hello world program. I actually want to see how the real world OS implement themselves.

A bootloader must be 512 bytes. No less no more.
And it must end with the standard PC boot signature: 0xAA55.
Also note a PC boots in 16 bits mode.
You need to load your kernel or secondstage bootloader from that code into memory, and then jump to that code (and maybe switch the CPU to 32 bits protected mode).
For instance (nasm):
BITS 16
; Your assembly code here (510 bytes max)...
jmp $
; Fills the remaining space with 0
times 510 - ( $ - $$ ) db 0
; Boot signature
dw 0xAA55

Thas the job of a boot loader. The bootloader should be present in the first 512 bytes of a HardDisk. This location is called MBR(Master boot record)
When bios loads it checks if the media contains the MBR. it verifies the MBR signature 0xAA55 which should be present as the last 2 bytes of MBR.
Then the Bios loads the BootLoader into RAM at address 0x7C00
Then the boot loader is the one who actually loads the kernal into memory, by reading the filesystem.
usually you cannot fit in all the code in 512 bytes, so there will be a secondory boot loader.
secondary bootloader will be loaded by your primary bootloader.
secondary boot loader loads IDT and GDT (Interupt vector table and Global descriptor table). Enables A20 gate to move into protected mode.
secondary boot loader loads the 32 bit kernel from disk into memory, then jumps to the kernel code
For more information you can download Linux kernel v0.01 (First version). Look how it is done. to my surprise the code to read the File system + the code to move into protected mode is fit into 512 bytes of code.

Related

Second firmware slot jumps to first slot during FreeRTOS first task run

I've created an application which has 2 firmware slots in its memory mapping. It works pretty fine and both slots are executed correctly based on a 32-bit sequencer number stored in FLASH.
The problem appears when I'm trying to use FreeRTOS. By default, firmware is compiled for the first slot... and there's no any problem in running this slot. However when the device starts firmware saved in the second slot, when RTOS starts its first task in prvPortStartFirstTask, then jumps to vPortSVCHandler it switches to the task in the first slot.
What am I doing wrong? I thought function addresses are relative after compilation, so there should be no difficulties running this application with 2 firmware slots.
EDIT
My flow during switching from bootloader to main application is as follows:
1. Check which firmware slot should be used.
2. Disable IRQs.
2. Copy vector table to RAM. That part of RAM is the same for both slots. During copying process I'm changing offset for each address, so they will be compatible with particular firmware slot. By default addresses don't have offset, it's removed in post-compiling stage.
3. Set stack pointer, according to the first word in vector table in RAM. That addresses is not changed while copying vector table to RAM.
4. Set SCB->VTOR.
5. Execute Data Sync Barrier DSB().
6. Jump to the Reset Handler from vector table copied to RAM.
EDIT 2
When I compile application with changed FLASH memory address range to the secondary slot, it works properly.
Is it possible compile code such that application will be PC independent, at least it will work in that case?
EDIT 3
# Generate position independent code.
-fPIC
# Access bss via the GOT.
-mno-pic-data-is-text-relative
# GOT is not PC-relative; store GOT location in a register.
-msingle-pic-base
# Store GOT location in r9.
-mpic-register=r9
However, now this slot stopped working.
I think my problem is similar to that one.
Generally, firmwares aren't built position independent, so I wouldn't trust that all "function addresses are relative after compilation". You compile firmware for a specific start location (either the first or the second firmware slot).
As for your main question, have you done anything to switch the interrupt handlers / interrupt vector from one firmware slot to the other? Or are you jumping to the first firmware's interrupt handlers when you call the SVC handler?
How to change the interrupt vector varies between architectures. For an stm32f429, you could perhaps look here

Start to writing a bootloader for stm32l0 in IAR

What are the appropriate steps to write add a custom bootloader for stm32l0 in IAR? The following questions are not clear:
Do I make a new IAR Project?
If yes, do I write the bootloader like a normal project and just change my original .icf file so there is a small ROM and an small RAM region for the bootloader?
if no, what things do I have to configure in the IAR proejct apart from icf file and code?
what other things do I need to think of?
I'm having trouble starting into this.
So the icf would be for the main project:
__region_ROM_start__ = 0x08000000;
__region_ROM_end__ = 0x08008FFF;
So the icf would be for the bootloader project:
__region_Bootloader_ROM_start__ = 0x08009000;
__region_Bootloader_ROM_end__ = 0x08009FFF;
and the same thing for about 0xFF of RAM?
You do not need to restrict the RAM - you can use all of it because when you switch to the application a new run-time environment will be established and the RAM will be reused.
The flash you reserve for the bootloader must be a whole number of flash pages starting from the reset address The STM32L0 has very small flash pages so there should be minimal waste, but you don't want to have to change it if your bootloader grows, because then you will have to rebuild your application code for the new start address and old application images will no longer be loadable. So consider giving yourself a little headroom.
The bootloader can be built just like any other STM32L0xx project; the application code ROM configuration must start from an address above the bootloader. So for example say you have a 1Kbyte bootloader:
Boot ROM Start: 0x0800 0000
Boot ROM End: 0x0800 03FF
Application Start: 0x0800 0400
Application End: Part size dependent.
The bootloader itself must have a means of determining that an update is available, if an update is available it must then read the application data and write it to the application flash memory, it must then disable any interrupts that may have been enabled, it may also be necessary to deinitialise any peripherals used (if they remain active when the switch to the application is made it may cause problems), then the switch to the application code is made.
It is possible if the bootloader and application both run from the same clock configuration to minimise the configuration in the application and rely on the bootloader. This is a small space saving, but less flexible. If for example you make the bootloader run using the internal RC oscillator it will be portable across multiple hardware designs that may have differing application speed and clocking requirements and different external oscillator frequencies
The switch to the application is pretty simple on Cortex-M, it simply requires the vector table to be switched to the application's vector table, then the program-counter to be loaded - the latter requires a little assembly code. The following is for Cortex-M3, it may need some adaptation for M0+ but possibly not:
Given the following in-line assembly function:
__asm void boot_jump( uint32_t address )
{
LDR SP, [R0] ;Load new stack pointer address
LDR PC, [R0, #4] ;Load new program counter address
}
The bootloader switched to the application image thus:
// Switch off core clock before switching vector table
SysTick->CTRL = 0 ;
// Switch off any other enabled interrupts too
...
// Switch vector table
SCB->VTOR = APPLICATION_START_ADDR ;
//Jump to start address
boot_jump( APPLICATION_START_ADDR ) ;
Where APPLICATION_START_ADDR is the base address of the application area; this address is the start of the application's vector table, which starts with the initial stack pointer and reset vector, the boot_jump() function loads these into the SP and PC registers to start the application as if it had been started at reset. The application's reset vector contains the application's execution start address.
Your needs may vary, but in my experience a serial bootloader (using UART) using XMODEM and decoding an image in Intel Hex format takes about 4Kb of Flash. On an STM32L0 you may want to use something simpler - 1Kb is probably feasible if you simply stream raw binary the data and use hardware flow control (you need to control data flow because erasing and programming the flash takes time and also stops the CPU from running because you cannot on STM32 write flash memory while simultaneously fetching instructions from it).
See also: How to jump between programs in Stellaris

IAP Bootloader could not load RTX application image

I have splitted software into two parts: Bootloader(without RTX), Application image with RTX.
But the bootloader could not load the application image with RTX.
The Flash settings are:
--------------------------------------------------------------------
start address size
IROM 1: 0x08000000 0x2800 - Bootloader (without RTX)
IROM 2: 0x08002800 0xD000 - Application Image (with RTX)
I have test 3 ways:
(1) Use another App without RTX. The bootloader could load the app successfully.
(2) Change the application with RTX project IROM setting. I change the application project IROM start address from 0x08002800 to 0x08000000. And I download the application image into flash from the address 0x08000000. Ihe image could run from 0x08000000 successfully.
(3) The application image IROM start address setting is 0x08002800. After downloading bootloader and app image into flash, I debug the app project in keil step by step. I found that there is a "osTimerthread stack overflow" error. Then the main thread stack is also overflowed. I have tried to increase the stack size, but it doesn't work.
I found that the app starks in the RTX kernel switching. All threads are in the waiting state, and are not running.
Ps, when I am debugging in the keil,test item(2) also have stack overflow errors during kernel initialization. The item(2) works fine till now. So I just put any information needed here.
This is the debugging picture for item (3).
Are you actually changing the linker script to link starting at 0x08002800 when using the bootloader or just loading the application (linked at 0x08000000) at an offset of 0x2800? Double check this (look in the map file) for your linked output to ensure that all your symbols are not linked in the 0x08000000 - 0x08002800 range.
Additionally, make sure you are using the correct entry point and stack pointer. The application's stack pointer should be at 0x08002800, and the reset vector will be at 0x08002804. Your bootloader will need to setup the MSP register with the correct stack pointer before jumping to the application. Here is some example code from ST's USB DFU bootloader:
typedef void (*pFunction)(void);
pFunction JumpToApplication;
uint32_t JumpAddress;
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (USBD_DFU_APP_DEFAULT_ADD + 4);
JumpToApplication = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) USBD_DFU_APP_DEFAULT_ADD);
JumpToApplication();
Additionally, depending on how much your bootloader configures before jumping to the application, you may need to 'deconfigure' certain peripherals. As an example, if you setup your clocks in the bootloader before deciding to jump to the application, you may run into problems in your application if it assumes that the clocks are in the default configuration already. Similar things can happen with the NVIC and SysTick if your bootloader is using these before jumping to the application.
Lastly, along the same lines as the previous section, the application may be making assumptions about the state of peripherals being default, but it also may be making assumptions that the peripheral defaults are correct. For example: SCB->VTOR has a default value (I believe it is always 0x00000000), and this points to the vector table. Your bootloader will be linked to have its vector table at that location. You'll need to make sure that when your application is starting up, it updates the VTOR register to point to the actual location of its vector table.
Hopefully one of these sections helps you identify the problem.

c166 bootloader write to internal flash

I'm writing a bootloader for a c166 chip, to be exact, the 169FH. The bootloader can currently open a TCP/IP Connection so a PC can send an Intel hex file to the bootloader. This intel hex file is saved in the RAM. After receiving the hex file it is read line by line to set the bytes to the correct location in the flash. The flash location where the bootloader is stored is ofcourse different from where the main program can be saved.
This are the first two lines of the intel hex file:
:0200000400C03A
:20100000CA11EE111212341258127A129A12BC12DE12001322134413601388138813881349
The first line is to get the highest 16 bits of the 32bit flash address, which is in this case 0x00C0
in the second line are the lower 16 bits of the 32 bit flash address, which is in this case 0x1000. This creates to total address of 0x00C01000, the byte written to that address should be 0xCA.
I'm trying to write the byte to that address using the following code:
uint8_t u8Byte = (uint8_t )XHugeStringToDec((const __huge char *)&Ext_RamFlashSave.Byte[u32Idx], 9+(u8ByteIdx*2), 2);
uint8_t *u8Address = (uint8_t*)((uint32_t)(u32ExtendedLinearAddress << 16) + (uint32_t)u16BaseAddress + (uint32_t)u8ByteIdx);
*u8Address = (u8Byte);
XHugeStringToDec() is a function to get the hexadecimal value from the intel hex string. I know this is going correct.
Ext_RamFlashSave.Byte is the array where the intel hex file is storedin.
The u32ExtendedLinearAddress variable is 0x0C00, and is set earlier. The u16BaseAddress is 0x1000 and is also set earlier in code.
The problem is in the last line:
*u8Address = (u8Byte);
I have verified that u8Address is indeed 0x0C01000 and u8Byte is indeed 0xCA. But when I monitor my flash address, I do not see the byte written.
I can imagine that it is some kind of write protection, but I cannot find out how to work around this, or do I need a different way to write to the Flash address?
More info of how the intel-hex file is build is described here:
https://en.wikipedia.org/wiki/Intel_HEX
I am not familier with the chip you said.
But for writing to flash, Generally, following algorithm is used:
Unlock the flash. This is usually sending specific data sequence. The flash I use right now has 0xA0A0-> delay of 1msec -> 0x0505. This will enable writing to flash
Clear the error flags. Flash has some error flags like Write error or read error. Also, check for Busy flag to make sure Flash is ready
Erase the Page. Yes... You have to erase the page before writing to it. Even if you want to change a single bit, You have to erase entire page.
Write the data (Finally) But make sure that endien-ness is correct. Sometimes you Controller is Little Endien and Flash is Big Endien. So you have to match to Flash's.
Lock the Flash. This is usually done by sending same sequence which used for unlocking. (Refer Datasheet)
You cannot write to a flash directly... Have to go through entire process. That's why Flash are slow.
Some flash support 8bit writing while some only 16bit or 32 bit etc. So, you have to send those many bits while writing.
When you have to modify a small portion of a page, Read the page in a buffer. Modify data in the buffer. Erase the page and write entire buffer.
If you are modifying a

How to correctly use a startup-ipi to start an application processor?

My goal is to let my own kernel start an application cpu. It uses the same mechanism as the linux kernel:
Send asserting and level triggered init-IPI
Wait...
Send deasserting and level triggered init-IPI
Wait...
Send up to two startup-IPIs with vector number (0x40000 >> 12) (the entry code for the application processor lies there)
Currently I'm just interested in making it work with QEMU. Unfortunately, instead of jumping to 0x40000, the application cpu jumps to 0x0 with the cs register set to 0x4000. (I checked with gdb).
The Intel MultiProcessor Specification (B.4.2) explains that the behavior that I noticed is valid if the target processor is halted immediately after RESET or INIT. But shouldn't this also apply to the code of the linux kernel? It sends the startup-IPI after the init-IPI. Or do I misunderstand the specification?
What can I do to have the application processor jump to 0x000VV000 and not to 0x0 with the cs register set to 0xVV00? I really can't see, where linux does something that changes the behavior.
It seems that I really misunderstood the specification: Since the application cpu is started in real mode 0x000VV000 is equivalent to 0xVV00:0x0000. It is not possible to represent the address just in the 16 bit ip register. Therefore a segment offset for the code segment is required.
Additionally, debugging real mode code with gdb is comparable complicated because it does not respect the segment offset. When required to see the disassembled code of the trampoline at the current position, it is necessary to calculate the physical location:
x/20i $eip+0xVV000
This makes gdb print the next 20 instructions at 0xVV00:$eip.

Resources