📜 ⬆️ ⬇️

Break STM8 stack

In the process of writing the STM8uLoader loader for STM8 microcontrollers, it became necessary to measure the stack depth.

Let us ask questions:


The amount of RAM memory and the depth of the stack for different models STM8 may vary.
Model STM8S103F3 was selected for the study.
')
The documentation on the STM8S103F3 gives the following data:
- stack depth 513 bytes;
- when reset, the SP pointer is initialized to the value 0x03FF (RAM END);
- the stack grows towards decreasing addresses.

The calculation shows that the lower limit of the stack is:

0x03FF - 513 = 0x01FF 

To break this boundary, you need to push a few more than 513 bytes on the stack.

The contents of the stack itself do not interest us. It is enough to know the contents of the SP stack pointer which should contain the address of the next RAM cell not occupied by the stack.
We will consistently place the bytes with any “push” command (for example, “push A”) and send the contents of the higher SPH and lower SPL byte of the SP pointer to the UART before each step.

Algorithm procedure:

1 Initialize the stack pointer with the value 0x03FF and configure the UART;
2 We are waiting for any byte from the terminal program;
3 Bytes received;
4 Send the contents of the SP pointer to the UART;
5 We push the contents of the battery with the command “push A”;
6 If the send cycles are less than 64, go to step 4;
7 If the send cycle is 64, go to step 2.

 ;  UART 9600/8N1 mov UART1_BRR2, #$00 ;     mov UART1_BRR1, #$0D ;  / mov UART1_CR2, #%00001100 ;   SP  $03FF ldw X, #$03FF ; X <= RAM END ldw SP, X ; SP <= X ;     wait_rx_byte: btjf UART1_SR, #5, wait_rx_byte ; ld A, UART1_DR ;   bset PB_DDR,#5 bset PB_CR1,#5 ldw Y, #64 ; Y <= 64 stack_cycle: ldw X, SP ; X <= SP ;  SPH  UART ; rlwa X ; A <- XH <- XL <- A ld A, XH ; A <- XH ld UART1_DR, A ; UART1_DR <= A wait_tx_byte_XH: btjf UART1_SR, #7, wait_tx_byte_XH ;  SPL  UART ; rlwa X ; A <- XH <- XL <- A ld A, XL ; A <- XL ld UART1_DR, A ; UART1_DR <= A wait_tx_byte_XL: btjf UART1_SR, #7, wait_tx_byte_XL ;  A   push A ; M(SP--) <= A decw Y jrne stack_cycle ;   bres PB_DDR,#5 bres PB_CR1,#5 jra wait_rx_byte 

Observe how the terminal program consistently receives the contents of the SP pointer, starting with 0x03FF:

  03 FF 03 FE 03 FD 03 FC 03 FB 03 FA 03 F9 03 F8 03 F7 03 F6 03 F5 03 F4 03 F3 03 F2 03 F1 03 F0 03 EF 03 EE 03 ED 03 EC 03 EB 03 EA 03 E9 03 E8 03 E7 03 E6 03 E5 03 E4 03 E3 03 E2 03 E1 03 E0 03 DF 03 DE 03 DD 03 DC 03 DB 03 DA 03 D9 03 D8 

After the value has reached 0x01FF (previously calculated stack boundary)
SP again took the value 0x03FF (stack closed in the ring)
and began to overwrite the oldest data

  02 0F 02 0E 02 0D 02 0C 02 0B 02 0A 02 09 02 08 02 07 02 06 02 05 02 04 02 03 02 02 02 01 02 00 01 FF 03 FF 03 FE 03 FD 03 FC 03 FB 03 FA 03 F9 03 F8 03 F7 03 F6 03 F5 03 F4 03 F3 03 F2 03 F1 03 F0 03 EF 03 EE 03 ED 03 EC 03 EB 03 EA 03 E9 

Now consider how the contents of the SP pointer will behave if you try to unload the contents from the stack indefinitely.

Algorithm procedure:

1 Initialize the stack pointer with the value 0x03FF and configure the UART;
2 We are waiting for any byte from the terminal program;
3 Bytes received;
4 Extract the contents of the stack with the command "pop A" in the battery;
5 Sends the contents of the SP pointer to the UART;
6 If the send cycles are less than 64, go to step 3;
7 If the send cycle is 64, go to step 2.

Paragraphs 4 and 5 of the algorithm and the “push A” command to the “pop A” command were reversed.
Despite the fact that we initialized the SP pointer to 0x03FF already after the first “pop A” command, the pointer took the value 0x01FF and continued to increase towards 0x03FF.

  01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 02 11 02 12 02 13 02 14 02 15 02 16 02 17 02 18 02 19 02 1A 02 1B 02 1C 02 1D 02 1E 02 1F 02 20 02 21 02 22 02 23 02 24 02 25 02 26 

Reaching the value of 0x03FF. after the next “pop A” command, the pointer again took the value 0x01FF and continued to increase towards 0x03FF.

  03 EF 03 F0 03 F1 03 F2 03 F3 03 F4 03 F5 03 F6 03 F7 03 F8 03 F9 03 FA 03 FB 03 FC 03 FD 03 FE 03 FF 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 02 11 02 12 02 13 02 14 02 15 

In the opposite direction, with an excessive number of pop (w) commands, the stack is also closed into a ring with a length of 513 bytes.

The stack in STM8S103F3 is linear until you break one of its bounds 0x01FF or 0x03FF.

As soon as you break one of the boundaries, the stack becomes a ring with a length of 513 bytes.
No matter where in the ring (in the addresses 0x01FF ... 0x03FF) there will be the top / bottom of the stack, we can put an unlimited number of bytes on the stack, but we can extract no more than 513 not damaged bytes (the most "fresh").

Now that the stack is localized in the addresses 0x01FF ... 0x03FF, it is time to break this range when initializing the SP pointer.

In paragraph 1 of the first procedure, we replace the value 0x03FF of the initialization of the SP pointer by the value 0x01FE.

We observe how the stack from the address 0x01FE went in the direction of decreasing addresses.

  01 FE 01 FD 01 FC 01 FB 01 FA 01 F9 01 F8 01 F7 01 F6 01 F5 01 F4 01 F3 01 F2 01 F1 01 F0 01 EF 01 EE 01 ED 01 EC 01 EB 01 EA 01 E9 01 E8 01 E7 01 E6 01 E5 01 E4 01 E3 01 E2 01 E1 01 E0 01 DF 01 DE 01 DD 01 DC 01 DB 01 DA 01 D9 01 D8 01 D7 

Reaching the address 0x0000, the stack went out of the RAM memory and penetrated the FLASH “memory” cells inaccessible for the STM8S103F3.

  00 16 00 15 00 14 00 13 00 12 00 11 00 10 00 0F 00 0E 00 0D 00 0C 00 0B 00 0A 00 09 00 08 00 07 00 06 00 05 00 04 00 03 00 02 00 01 00 00 FF FF FF FE FF FD FF FC FF FB FF FA FF F9 FF F8 FF F7 FF F6 FF F5 FF F4 FF F3 FF F2 FF F1 FF F0 FF EF 

No calls of subroutines or interrupts are possible after the pointer leaves the RAM memory. True, somewhere in the depths of the stack, the most “ancient” data still remained, which were fortunate enough to remain in RAM memory.

Now we will try to extract data from the stack with “forbidden” (outside the range 0x01FF ... 0x03FF) initialization of the SP pointer.

Let's start with addresses outside of RAM. In paragraph 1 of the second procedure, we replace the value 0x03FF of the initialization of the SP pointer by the value 0xFFF8.

Observe how the stack went into RAM memory.

  FF E9 FF EA FF EB FF EC FF ED FF EE FF EF FF F0 FF F1 FF F2 FF F3 FF F4 FF F5 FF F6 FF F7 FF F8 FF F9 FF FA FF FB FF FC FF FD FF FE FF FF 00 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 00 0A 00 0B 00 0C 00 0D 00 0E 00 0F 00 10 

Crossing the lower border 0x01FF, the stack entered its territory.

  01 E9 01 EA 01 EB 01 EC 01 ED 01 EE 01 EF 01 F0 01 F1 01 F2 01 F3 01 F4 01 F5 01 F6 01 F7 01 F8 01 F9 01 FA 01 FB 01 FC 01 FD 01 FE 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 

Reaching the address 0x03FF, the stack is closed in a ring.

  03 E9 03 EA 03 EB 03 EC 03 ED 03 EE 03 EF 03 F0 03 F1 03 F2 03 F3 03 F4 03 F5 03 F6 03 F7 03 F8 03 F9 03 FA 03 FB 03 FC 03 FD 03 FE 03 FF 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 

Findings:

The stack in STM8S103F3 is capable of performing its duties only within the range 0x01FF ... 0x03FF.

To get the greatest linear depth of the SP stack pointer in the STM8S103F3, you need to initialize with the value 0x03FF.

The stack in STM8S103F3 is linear until you break the lower bound 0x01FF.
As soon as you break the lower bound, the stack becomes a ring length of 513 bytes.

Source: https://habr.com/ru/post/417203/


All Articles