PSoC4 confidential

Unofficial PSoC4 manual/FAQ/errata/rumor & wild guess repository.

This document will serve as a repository of undocumented knowledge on the PSoC4 family of chips. I will update it as I learn more things. You really are unlikely to get much use of this without first acquainting yourself with my previous PSoC4 article. Revision history follows:

  1. Initial publishing (some TODOs still exist)
  2. Register docs added, AV pairs semi-documented (some TODOs still exist)
  3. Found how to and added code to show you how to enter non-NMI privileged mode on PSoC4000!
  4. Added protected-mode memory map
  5. Added SCB investigation in PSoC4000

Table of contents

Why does it matter?

With this info you can use the chip to its full power, write flash in new ways, unlock extra storage, and have a fun time reverse engineering more things. Enjoy :)


# Name Permissions Availability Parameters Notes
CPU Debugger 4000 4100 / 4200 4100M / 4200M 4000S CYBL 10x6x Param in reg Struct
Virgin Kill Prot Open Virgin Kill Prot Open
0x00 Get_Silicon_ID u16 key; documented
0x01 Write_NVL_byte [1] [1] u16 key;
u8 idx;
u8 val;
Write "val" into latch whose index is "idx". Survives reboots but not power cycles. Not sure why
0x02 Load_NV_latches_from_HW [1] [1] u16 key; Loads NV latches from wherever they are stored in the chip. Overwrites anything written using Write_NVL_byte()
0x03 Read_NVL_byte [1] [1] u16 key;
u8 idx;
u8 val;
Read latch whose index is "idx" into "val"
0x04 Load_Write_Latches u16 key;
u8 offset;
u8 macro;
u8 loadSzMinus1;
u8 reserved[3];
u8 data[loadSzMinus1+1];
0x05 Write_Row u16 key;
u16 rowID;
write is only performed if write latches have any nonzero bytes
0x06 Program_Row u16 key;
u16 rowID;
write is only performed if write latches have any nonzero bytes
flash is not checked for being blank
0x07 Nonblocking_Write_Row u16 key;
u16 rowID;
write is only performed if write latches have any nonzero bytes
0x08 Nonblocking_Program_Row u16 key;
u16 rowID;
write is only performed if write latches have any nonzero bytes
flash is not checked for being blank
0x09 Nonblocking_Resume u16 key; documented
write is only performed if write latches have any nonzero bytes
flash is not checked for being blank
0x0a Erase_All u16 key; documented
0x0b Checksum u16 key;
u16 row;
any "row" >= 0x8000 checksums all code flash
else any "row" >= 0x4000 checksums that row of sflash
else given code flash row is checksummed
0x0c Flash_Analog_Read u16 key;
u8 level1;
u8 flags;
u32 addr;
u8 levels[];
u8 data[];
If chip has only one flash macro, "level1" is used for level "levels" is 0-sized, else levels[] is a multiple of 4 and data is written after it.
Flags: 0x20 - read single byte (else row), 0x10 - read sflash (else code flash), 0x01 - set "0" threshold, else "1". Internally calls Flash_Analog_Read_Mode_Enter() and Flash_Analog_Read_Mode_Exit() as needed.
0x0d Write_Protection u16 key;
u8 prot;
u8 macro;
undocumented option: write prot to 0xDE and macro to 0xFA to returnchip to virgin state
0x0e Memset_Word u16 key;
u32 start;
u32 end;
u32 word
a memset, but word-sized
0x0f Copy_Some_Code_to_Ram




u16 key;
u32 word;
If SPCIF_GEOMETRY.DE_CPD_LP is not set, set it and copy the following bytes [3] to ram + ofst. Besides that, write "word" to ram + offset + 0x24, and 0xAE to ram + offset + 0x20
0x10 Flash_Rubber_To_Road




/*no key*/ Calls flash_rubber_to_road(*(u8)(ram + ofst + 0), *(u8)(ram + ofst + 1))
0x11 Flash_Analog_Read_Mode_Enter




/*no key*/ Address you intend to read should be written to ram + ofst
0x12 Flash_Analog_Read_Mode_Exit /*no key*/
0x13 Init_Chip u16 key; Verifies SFLASH checksum and sets some sane defaults in many registers
0x14 Unknown u16 key; On all chips i've seen this simply returns error 0xf0000010
0x15 Config_Flash_Clock [2] u16 key; documented
0x16 Read_Clock_Regs u16 key;
u32 *dst;
CLK_IMO_CONFIG -> dst[0]; CLK_SELECT -> dst[1], CLK_IMO_TRIM2 -> dst[2], CPUSS_FLASH_CTL -> dst[3]
0x17 Clocks_Manual_Setup u16 key;
u32* cfg;
cfg: {u32 v_CLK_IMO_CONFIG; u32 v_CLK_SELECT; u32 v_CLK_IMO_TRIM2; u32 v_CLK_IMO_CONFIG;}; Writes those regs, configures clock trims to use them as requested. Config_Flash_Clock() is just a special case of a call to this.
0x18 Write_Sflash_Row u16 key;
u32 rowNum;
4 rows are available. They can be read at 0x0FFFF200. Always blocks and always performs an erase first. There is no nonblocking/nonerasing version.
0x19 Unknown u16 key; still working on this
0x1a Unknown_And_Boring u16 key; Write 0x00000001 to 0x20000000 and to 0x20000004, set SYSARG to 0xA0000000 | *(u32*)0x200000c0
0x1b Chip_Reset u16 key; Literally just resets the chip
1: API is implemented, but as no latches exist, it always returns 0xf000000f
2: API is implemented, but since clocks are configured automatically as needed, always just returns error 0xf0000013
3: the copied code is:
PSoC4000/PSoC4100/PSoC4200: PSoC4000S/PSoC4100M/PSoC4200M/CYBL10x6x:
0xB570 - PUSH {R4-R6,LR} 0x4D06 - LDR R5, [PC, #0x18] //label_3 0x2001 - MOV R0, #0x01 0x686E - LDR R6, [R5, #0x04] 0x4306 - ORR R6, R0 0x2400 - MOV R4, #0x00 label_1: 0x6828 - LDR R0, [R5] 0x28AE - CMP R0, #0xAE 0xD101 - BNE label_2 0x47B0 - BLX R6 0x602C - STR R4, [R5] label_2: 0xBF30 - WFI 0xE7F8 - B label_1 0x0000 label_3: DCD ram + offset + 0x10
0x4D05 - LDR R5, [PC, #0x14] //label_3 0x2001 - MOV R0, #0x01 0x686C - LDR R4, [R5,#0x04] 0x4304 - ORR R4, R0 0x2600 - MOV R6, #0x00 label_1: 0x6828 - LDR R0, [R5] 0x28AE - CMP R0, #0xAE 0xD101 - BNE label_2 0x47A0 - BLX R4 0x602E - STR R6, [R5] label_2: 0xBF30 - WFI 0xE7F8 - B label_1 label_3: DCD ram + offset + 0x1C DCD ram + offset + 0x20

Syscall error values

Number Documented Meaning
0xf0000001 syscall not avail in current chip protection mode
0xf0000002 nvlatch index out of bounds
0xf0000003 flash latch offset or length out of bounds
0xf0000004 rowID out of bounds
0xf0000005 rowID refers to a protected row
0xf0000006 ??? I have not seen this one ???
0xf0000007 no more resume (all background tasks are done)
0xf0000008 resume pending (no api can be called until you call resume)
0xf0000009 resume called too soon (background task ongoing)
0xf000000a memory nonzero (likely flash erase failed)
0xf000000b no such syscall
0xf000000c syscall key mismatch
0xf000000d ??? I have not seen this one ???
0xf000000e invalid address range (start > end)
0xf000000f nvlatches not supported on this device
0xf0000010 ??? Not sure. Syscall number 0x14 on 4000S/4200M/BLE/4000 always returns this error
0xf0000011 ??? I have not seen this one ???
0xf0000012 flash clocking error. Forgot to call Config_Flash_Clock()?
0xf0000013 clock already up - returned by chips which do not need a call to Configure_Clock()
0xf0000014 ??? In weird mode (returned from some api (0x0a, 0x0d, 0x15, 0x17, 0x18) when CPUSS_PROTECTION & 0x40000000)

A Unique chip ID exists!

In SFLASH there are a few values that are of interest. A 24-bit Lot ID for the silicon wafer that this chip came from at 0x0FFFF178, The 8-bit wafer number in the lot that this chip came from at 0x0FFFF17B, and the 8-bit X and 8-bit Y coordinates on the wafer that this chip came from (row and colum numbers) at 0x0FFFF17C and 0x0FFFF17D respectively. Clearly in any given family this will be unique as no two chips could occupy the same physical location on the same wafer in the same lot. I am not sure if this is unique inter-family, so you should also, together which these values, use the Silicon ID (16-bit), Revision ID (8-bit), and the family ID (12-bit) values from the SiliconID syscall. Together these form a 24+8+8+8+16+8+12 = 84-bit unique chip ID. Keep in mind that while this will be unique, it is also highly predictable. For example, the 5 PSoC4000 chips I ordered from Mouser only differ in wafer X coordinate, and by exactly one. So you should not use this directly in a place where guessing it correctly would compromise safety. If in doubt, securely hash with salt to obtain a harder-to-guess uniqie ID. A quick-and-dirty function to get the raw unique ID might look like this:

void psoc4getUniqId(uint8_t *dst) //produces 11 bytes { volatile uint8_t *uniqNfo = (volatile uint8_t*)0x0FFFF178 uint32_t i; //first syscall to get family and silicon IDs CPUSS->SYSARG = 0xD3B6; CPUSS->SYSREQ = 0x80000000; //collect family ID *dst++ = (CPUSS->SYSREQ >> 8) & 0x0F; *dst++ = CPUSS->SYSREQ; //collect silicon ID *dst++ = CPUSS->SYSARG >> 8; *dst++ = CPUSS->SYSARG; //collect revision *dst++ = CPUSS->SYSARG >> 16; //collect data from sflash (luckily it is sequential in there) for (i = 0; i < 6; i++) *dst++ = *uniqNfo++; }

My SPCIF_GEOMETRY register lied to me!

If you read SPCIF_GEOMETRY in some members of the PSoC4 family, you will see it claiming 16K of flash, while the chip only has 8K (as per datasheet, cypress tools, and syscalls). In some members of the PSoC4200M family, you'll see the register claim twice the RAM amount the datasheet says you have. This is not a bug. Well, it kind of is and kind of is not. It is market segmentation at work. The values you see in SPCIF_GEOMETRY are the actual amounts of flash, SFLASH, RAM, and ROM that your chip has. If your datasheet claims less, the remainder is fully functional, but disabled for market segmentation resons. This is done using WOUNDING. With some effort, you can undo this and gain use of extra flash and RAM permanently.


Wounding is Cypress's method of market segmentation. It turns out that for small sizes of flash and RAM, developing a die with differing amounts and making them costs more than just giving each die the same amount (the larger). But this means you either lower margins or do not sell a low-end product. To avoid both, you can just hide some of the RAM and flash and claim the chip has less. Since using the same die for both is actually cheaper, this allows the company to capture the extra money from people who do want more than the lower amount of flash or RAM.

In PSoC4 chips, this is implemented using WOUNDING registers. These registers are write-once in a stange way. You can always increment them, but you cannot decrement them. Reset clears them. Thus, the higher the value in the WOUNDING register, the more of flash and RAM is hidden and inaccessible. There are, in fact, a few WOUNDING registers. The one for flash and RAM is CPUSS_WOUNDING, which exists at [CPUSS + 0x1C]. In here you'll find a 3-bit bitfield at bits 20..22 for flash limiting, and a 3-bit bitfield at bits 16..18 for RAM limiting. The value (N) you place in those bitfields determines how much ram and flash is left accessible. The formula is easy. Accesible = Total / (2 ^ N). RAM and flash limits may differ. If you try to read this register, you'll find that it is not readable to you. It is a privileged-only register. However, unless you changed it (unlikely as you cannot even read it), the boot process probably set the values for you. How does it know what to? SFLASH! At address 0x0FFFF140 you'll find a word whose contents will be copied to CPUSS_WOUNDING. How is the actual "inaccesibility" implemented? Same way that privileged-only registers are.

It is not only flash and RAM that can get artifically limited. The SAR ADC, in chips that have it, can also be artificially limited. The register for that is SAR_WOUNDING, and it is at [SAR + 0xF04]. privileged-only, of course. I have not seen any SROM code that writes this, but, then again, I have not read SROMs of all the members of the PSoC4 family.

Yes, there are undocumented registers. No you cannot use them [easily].

Like most chips, PSoC4 has many undocumented secret registers. Some fascinating, many mundane. However, unlike most chips, PSoC4's secret registers are not easy to access. Cypress gated access to most using the HPROT[1] flag. The practical upshot of this is that to normal code running on the chip, it looks like these registers do not exist. This is accomplished by repling to the AHB transaction with HRESP[0] = 1 (aka: ERROR). The core understands this as accessing a nonexistent register and triggers a HardFault (or a LOCKUP if at NMI or HardFault priority). I refer to these as privileged-only registers, and the chip is full of them. Every peripheral on the chip has some. Older Cypress docs documented a few of them, like CPUSS_WOUNDING. Newer docs do not mention them at all. Many were never documented at all, for example the insides of the SPCIF module. I will document registers here as I reverse engineer them. Please, however, do keep in mind that these are only my guesses, substantiated only by disassembled SROM code. Any or all of this could be subtly incorrect, incomplete, inaccurate, and/or simply wildly wrong. That being said, I have successfully used most of these as I documented them. The section on privileged execution is a good start on experimenting with these.

What does the boot process do?

Depending on the chip, few activities hapen. I will outline the generic stuff here, and provide specifics as I learn them.

  1. Check RAM for a magic value, and if so, assume it contain factory test info and data and execute some code (only later PSoC4 members)
  2. Read chip protection value from SFLASH and configure debugger limits (disable access to almost all of the memory space), if in protected mode
  3. Configure SWD pins (based on protection level and SFLASH settings)
  4. If there is RAM wounding to use, move stack into an area that will be accessible after wounding
  5. Apply wounding
  6. Checksum SFLASH and verify checksum matches
  7. Initialize various registers using AV pairs (that is what SFLASH offsets 0x80-0x140 contain in packed form).
  8. Check if debugger has requested a stop (TST_MODE register top bit)
  9. Check if SFLASH checksum failed, if so, reset (this will loop forever)
  10. Drop privileges (leave privileged mode)
  11. If stop was requested, loop in SROM
  12. If SFLASH has special boot address dedicated (usually not), jump there
  13. If we got this far, boot user code by loading [0x00] -> SP and [0x04] -> PC

AV pair format?

AV pairs (address-value pairs) are values for various hardware registers that are initialized on boot from SFLASH. Since SFLASH contains values that are usually unique per chip and SROM contains others that are always the same, we can assume that all registers written to by AV pair data are different between different chips of the same family.

32-bit AV pairs (0x0FFFF100..0x0FFFF13F) are simple: they literally contain 32-bit address and then 32bit data. Address of 0xffffffff acts as terminator of this list

8-bit AV pairs (0x0FFFF080..0x0FFFF0FF) are complicated and encoded differently on each chip in the family. The only thing they have in common is that they always write 8-bit values to initialize 32-bit registers at 0x4000FF00 or later. Most of them write towards the end of each peripheral's address space (where various calibration registers are). What follows are rough decoders of the 8-bit AV pairs for the old format (4000/4100/4200) and new format 4000S/4100M/4200M. You can tell them apart by looking at the last byte before the zeroes begin. Here 0x37 means old format, 0xff means new. The code is written in this style because it is written to closely match assembly listing and not to be pretty.

Old format decoder New format decoder
Params are "vals" (0x0ffff080) and "dataLen" (128)
uint32_t val, len, i, tmp = 0, strVal, strAddr; uint8_t *p = vals, *nextAddr; while (1) { val = *p; len = val & 7; nextAddr = p + len; if (nextAddr >= vals + dataLen) break; if (val == 0x37) break; for (i = 0; i < len; i++) { strAddr = 0x4000FF00; strVal = p[i + 1]; if (((tmp >> 8) & 0xF8) == (val & 0xF8)) { tmp++; strAddr += (tmp & 0xff) << 2; } else { tmp = val << 8; } if (val & 0x08) strAddr += 0x00100000; strAddr += (val & 0xf0) << 12; printf("0x%02X -> [0x%08X]\n", strVal, strAddr); } p = nextAddr + 2; }
uint32_t val, len; uint8_t *p = vals, *nextAddr; while (1) { uint32_t i, strVal, strAddr = 0x4000FF00; val = p[0]; len = p[1]; nextAddr = p + len; if (nextAddr >= vals + dataLen) break; if (val == 0xff) break; strAddr += (val & 0xE0) << 15; strAddr += (val & 0x1F) << 16; for (i = 0; i < len; i++) { strVal = p[i + 2]; printf("0x%02X -> [0x%08X]\n", strVal, strAddr + i * 4); } p = nextAddr + 2; }

PSoC4000 SCB regs look just like other PsoC4 family members. Does that mean it can do UART & SPI too?

I looked into this. It seems like it should. It sort of works. If you clock it, it takes a byte into the shift register even (as per TX_FIFO_STATUS). But that is as far as it goes. I checked the SROM in case there is a register that cripples (WOUNDS?) the SCB, but I found nothing that looks like it. Sorry.

What exactly does "protected" mean?

Protected mode is meant to protect the code in the chip form readout. As you saw in the syscall table, most API are not available in protected mode. Also, most memory is off-limits to the debugger. What is of interest is exactly what memory is and is not accessible. This is not documented and I see no ways to tweak this. I did a full scan (over about a week) of the address space on the PSoC4000S. The accessible areas are CPUSS, TST, and the top level rom table. Here are the values:

[40030014] = 0x00000004 [40100004] = 0x00000000 [40100008] = 0xA0000000 [f0000000] = 0xF00FF003 [f0000004] = 0x0FFFF002 [f0000008] = 0x00000000 ... = 0x00000000 [f0000fc8] = 0x00000000 [f0000fcc] = 0x00000001 [f0000fd0] = 0x00000000 ... = 0x00000000 [f0000fdc] = 0x00000000 [f0000fe0] = 0x000000A9 [f0000fe4] = 0x00000040 [f0000fe8] = 0x0000002B [f0000fec] = 0x00000010 [f0000ff0] = 0x0000000D [f0000ff4] = 0x00000010 [f0000ff8] = 0x00000005 [f0000ffc] = 0x000000B1

How does one dump the SROM?

I've automated this to the exteme in CortexProg (look for it out soon). At this point it is a two-step process. First step automatically finds an exploitable load in the first part of the SROM and uses it to dump said first half. Second step needs an offset of an exploitable gadget in the first half to use to dump the rest. Human intervention is needed once per chip family to find a gadget. This offset is fed to CortexProg to use to dump the rest and enable the "Read As Provileged" function on PSoC to allow access to privileged-only registers for further investigations. What does it look like? Step 1 looks like this.

$> sudo CortexProg -Spsoc4xxx_read_minirom -A 0x10000000 -L 512 > psoc_4000s_srom_mini.bin SWD FW VER: 0x00020005, flags 0x00000000, maxXfer: 1032 Cortex-M0+ found base=0xf0000000 periphId = 0x00000000102b40a9 JEDEC = {0, 0x34, 0x0a9} Chip is by 'Cypress', model 0x0A9 CPUID = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000a9, 0x00000040, 0x0000002b, 0x00000010, } CPU ID found -> 'PSoC 4xxx' Using scriptfile 'SCRIPTS/PSoC4xxx.20000000.swds' Auto-exploiting PSoC4 SROM... SETUP: Step 0 ... Upload complete Step 1 ... Trampolines set up Step 2 ... Splines reticulated ... CPUSS auto-found at 0x40100000 Step 3 ... Regs set LET'S GO: Step 0 ... PC not in position Step 1 ... Syscall not found anywhere Step 2 ... Syscall not found anywhere Step 3 ... Syscall not found anywhere Step 4 ... Syscall found! Inspecting 0 Potential base reg: R4 (ofst 0x04) Will exploit suspected load with target(s): [R4, #0x04] Dumping ... 0x10000000-0x10000200 SUCCESS

Step 2 is a bit more work and looks like this

$> for addr in {268435456..268451840..4}; do sudo CortexProg -Spsoc4xxx_read_word_as_priviledged -L 4 -A $addr 2>&1 | grep "\[" >> psoc_4000s_srom_full.txt; done $> sed 's/[^=]*= 0x\(..\)\(..\)\(..\)\(..\)/printf "\\x\4\\x\3\\x\2\\x\1"/g' < psoc_4000s_srom_full.txt > psoc_4000s_srom_full.cmd $> bash psoc_4000s_srom_full.cmd > psoc_4000s_srom_full.bin

I want root (how to become privileged)

Keep in mind that normally execution in privileged mode is rather limited in a number of ways. To start with, I have not yet found a way to execute in Thread mode as privileged generically on all devices. Currently there needs to be a per-device exploit. The CPU does boot in that way, but I have yet to find a way to do this on demand easily. I did find specific per-family exploits for some devices. See below. For devices without these fancy nice exploits, there is a way to run in NMI-mode privileged mode. What that means is that you're running at NMI priority. What does this cause? Effectively interrupts are disabled while you're running privileged code, since you're executing at a higher priority. But that's not all. You're also at higher priority than HardFaut, so if you mess up, the CPU enters LOCKUP state. Your hard fault handler does not run either. And if that is not enough, debugging is also inhibited while in privileged mode, so do not expect your debugger's help. It seems to me that while in privileged mode, the debugger limitations are similar to those that are applied when chip is in protected mode. That means that you can only access very limited things, noe of them useful. All of this combined makes execution in privileged mode a rather large pain. But sometimes, it is worth it :).

But there are specific exploits you can use! So, here are the gems you came for. This code will allow you to enter privileged mode on some of the PSoC4 family chips. Not only that, you will not be in NMI mode, so interupts will work and HardFault handlers will work if you mess up! Keep in mind that you still get no debugging! Your debugger (whatever it is) will either freak out or give up as soon as you enter privileged mode. This is by design and cannot be avoided as far as I can tell. This exploit was quite a difficult thing to achieve, but here it is! To enter privileged mode, just call this function. Upon its return you'll be privileged. To exit privileged mode, just write 0 to CPUSS->SYSREQ. This will not work on any other PSoC4 chip family (do not bother trying), and might not even work on other SROM revisions (YMMV). I do not currently have such a simple exploit for other chip families - there I am still stuck doing privileged experimentation in NMI mode. I will try to generalize this exploit as much as I can: to other SROM revisions (if they exist) and other chip families. Stay tuned. Since NMI-mode privileged world is a much harsher world to live in, I will not publish the NMI exploits for those chips until I find one as convenient as this one for those chips. As with all other code here, this is licensed as: free to use for non-commercial use only. No commercial use without my prior approval. Allrighty then, here are the magic bytes:

4000 4100/4200 4000S 4100M/4200M CYBL10x6x
.globl privilegedEnter privilegedEnter: push {r4, lr} mov r4, sp lsr r0, r4, #3 bcc 1f push {r0} 1: mov r0, #0xE1 lsl r0, #8 add r0, #0xB6 mov r1, sp sub r1, #0x44 mov r2, r1 mov r3, #0x10 lsl r3, #16 add r3, #0x0D lsl r3, #8 add r3, #0x2F push {r0-r3} mov r1, sp mov r2, #0x40 lsl r2, #8 add r2, #0x10 lsl r2, #16 mov r0, #0x80 lsl r0, #24 add r0, #0x0e str r1, [r2, #8] dsb str r0, [r2, #4] dsb mov sp, r4 pop {r4, pc}
.globl privilegedEnter privilegedEnter: push {r4, lr} mov r4, sp lsr r0, r4, #3 bcc 1f push {r0} 1: mov r0, #0xE1 lsl r0, #8 add r0, #0xB6 mov r1, sp sub r1, #0x44 mov r2, r1 mov r3, #0x10 lsl r3, #16 add r3, #0x0D lsl r3, #8 add r3, #0x47 push {r0-r3} mov r1, sp mov r2, #0x40 lsl r2, #24 mov r0, #0x80 lsl r0, #24 add r0, #0x0e str r1, [r2, #8] dsb str r0, [r2, #4] dsb mov sp, r4 pop {r4, pc}
.globl privilegedEnter privilegedEnter: push {r4, lr} mov r4, sp lsr r0, r4, #3 bcc 1f push {r0} 1: mov r0, #0xE1 lsl r0, #8 add r0, #0xB6 mov r1, sp sub r1, #0x44 mov r2, r1 mov r3, #0x10 lsl r3, #16 add r3, #0x13 lsl r3, #8 add r3, #0xCD push {r0-r3} mov r1, sp mov r2, #0x40 lsl r2, #8 add r2, #0x10 lsl r2, #16 mov r0, #0x80 lsl r0, #24 add r0, #0x0e str r1, [r2, #8] dsb str r0, [r2, #4] dsb mov sp, r4 pop {r4, pc}
.globl privilegedEnter privilegedEnter: push {r4, lr} mov r4, sp lsr r0, r4, #3 bcc 1f push {r0} 1: mov r0, #0xE1 lsl r0, #8 add r0, #0xB6 mov r1, sp sub r1, #0x64 mov r2, r1 mov r3, #0x10 lsl r3, #16 add r3, #0x0E lsl r3, #8 add r3, #0x3D push {r0-r3} mov r1, sp mov r2, #0x40 lsl r2, #8 add r2, #0x10 lsl r2, #16 mov r0, #0x80 lsl r0, #24 add r0, #0x0e str r1, [r2, #8] push {r2-r7} dsb str r0, [r2, #4] dsb pop {r2-r7} mov sp, r4 pop {r4, pc}
.globl privilegedEnter privilegedEnter: push {r4, lr} mov r4, sp lsr r0, r4, #3 bcc 1f push {r0} 1: mov r0, #0xE1 lsl r0, #8 add r0, #0xB6 mov r1, sp sub r1, #0x44 mov r2, r1 mov r3, #0x10 lsl r3, #16 add r3, #0x10 lsl r3, #8 add r3, #0x51 push {r0-r3} mov r1, sp mov r2, #0x40 lsl r2, #8 add r2, #0x10 lsl r2, #16 mov r0, #0x80 lsl r0, #24 add r0, #0x0e str r1, [r2, #8] dsb str r0, [r2, #4] dsb mov sp, r4 pop {r4, pc}
© 2012-2017