In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "how to understand ARM handling exceptions". Interested friends may wish to have a look at it. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn "how to understand ARM handling exceptions"!
1. Exception (Exception)
Exception is the most important knowledge point to understand the operation of CPU. Almost every processor supports specific exception handling, and interrupt is one of the exceptions. Sometimes we measure the real-time performance of an operating system by looking at the minimum response interrupt time of os and the number of response interrupts per unit time.
Second, the source of anomaly
In the ARM architecture, there are seven types of exception handling. When an exception occurs, the processor sets the PC to a specific memory address. This address is placed within a specific range of addresses called vector tables (vector table), whose entrances are jump instructions that jump to subroutines that specifically handle an exception or interrupt.
1. Anomaly source classification
To enter the exception mode, there must be an exception source. ARM stipulates that there are 7 exception sources:
1.reset reset exception
The exception is entered when CPU is first powered on or after the reset restart key is pressed, and the exception is handled in administrative mode.
2.irq/fiq General / Quick interrupt request
CPU and external devices are independent hardware execution units. CPU manages and schedules all devices. If CPU wants to know the running status of external devices, either CPU regularly checks the specific registers of external devices, or let external devices "interrupt" CPU when they need CPU interference processing, and let it handle external device requests. There is no doubt that the second way is more reasonable. You can let CPU "concentrate" to work, where the "interrupt" operation is called interrupt request, according to the emergency of the request, interrupt request is divided into general interrupt and fast interrupt, fast interrupt has the highest interrupt priority and minimum interrupt delay, usually used to deal with high-speed data transmission and medium data recovery processing of the channel, such as DMA, most peripherals use general interrupt request.
3. Prefetch instruction abort exception
The exception occurs in the CPU pipeline fetch phase, and if the target instruction address is an illegal address to enter the exception, the exception is handled in abort exception mode.
4. No instruction exception defined
The exception occurs in the decoding phase of the pipeline technology. if the current instruction cannot be recognized as a valid instruction, an undefined instruction exception is generated and the exception is handled in an undefined exception mode.
5. Software interrupt instruction (swi) exception
This exception is generated by the application itself, and is used when the user program applies for access to hardware resources, such as the printf () print function. To print the user data to the display, the user program must apply to use the display to print, and the user program does not have the right to use the peripheral hardware, so it can only use the software interrupt instruction to switch to the kernel state and access the peripheral hardware through the kernel code of the operating system. The kernel mode works in privileged mode, and the operating system prints user data to the display in privileged mode. The purpose of doing this is nothing more than to protect the security of the operating system and the rational use of hardware resources, and the exception is handled in management mode.
6. Data abort access exception this exception occurs when the address of the data to be accessed does not exist or is illegal, and the exception is handled in abort exception mode.
2. Exception priority of ARM
Reset → Data abort → FIQ → IRQ → Prefetch abort → Undefined instruction/SWI .
3. The reason why FIQ is faster than IRQ
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Fiq has higher priority than irq.
The FIQ vector is at the end of the vector table, and exception handling does not need to jump.
FIQ has 5 more private registers (r8-r12) than IRQ, and there are fewer stack-off operations during interrupt operations.
Third, the hardware operation of exception occurrence
After the exception occurs, the operation steps of the arm core can be summarized as 4 big steps and 3 small steps.
1. 4 big steps and 3 small steps
1. Save execution status: copy the CPSR to the SPSR in the exception mode that occurs
two。 Mode switching:
The CPSR mode bit is forced to be set to the value corresponding to the exception type
The processor enters ARM execution mode
Disable all IRQ interrupts and prohibit FIQ interrupts when entering FIQ fast interrupt mode
3. Save return address: save the address of the next instruction (interrupted program) in LR (LR_excep in exception mode).
4. Jump into the exception vector table: force the value of PC to be set to the corresponding exception vector address and jump to the exception handler.
2-step detailed explanation
1. Save execution status
The execution state of the current program is saved in CPSR. When an exception occurs, it is necessary to save the execution state in the current CPSR to the SPSR in the exception mode. When the exception returns in the future, it will return to CPSR and resume the execution state.
two。 Mode switching
The hardware automatically writes the exception code to the M [4:0] mode bit in CPSR according to the current exception type, so that CPU enters the corresponding exception mode. At the same time, CPU will turn off the interrupt IRQ (set the CPSR register I bit) to prevent the interrupt from entering, and if the current fast interrupt FIQ exception, turn off the fast interrupt (set the CPSR register F bit).
3. Save return address
The current program is interrupted by an exception and switched to the exception handler. After the exception is handled, it is returned to the current interrupted mode to continue execution, so it is necessary to save the address of the next instruction currently executing to LR_excep (LR in exception mode, there is no LR_excep register, in order to facilitate readers to understand, add _ excep, the following reasons are the same), due to the different exception modes and the pipelining technology adopted by the ARM kernel The return address is calculated according to the exception pattern in the exception handler.
4. Jump into the anomaly vector scale
This is done automatically by the CPU hardware, and when an exception occurs, CPU forces the value of the PC to be changed to a fixed memory address called the exception vector.
4. Anomaly vector scale
The exception vector table is a specific memory address space, and each ARM exception corresponds to a word length space (4Bytes), which is exactly the length of a 32-bit instruction. When an exception occurs, CPU forces the value of PC to be set to the fixed memory address corresponding to the current exception.
1. Exception vector scale:
Anomaly vector scale
When an exception occurs, the hardware automatically completes the operation of jumping into the exception vector table, and the rest of the exception handling task is left to the programmer. As can be seen from the above table, the exception vector is a fixed memory address, and we can complete the exception handling by writing a jump instruction to that address and asking it to jump to the entrance of our own defined exception handler.
Anomaly vector scale
It is because of the existence of exception vector table that hardware exception handling is organically linked with programmers' custom handlers. The 0x00000000 address in the exception vector table is the reset reset exception. The reason why it is 0 address is that CPU automatically loads instructions from the 0 address when it is powered on, so it can be seen that installing the reset exception at this address is also designed before and after. I have to sigh the greatness of the CPU designer, followed by the other seven exception vectors, each of which occupies four bytes, which is exactly the size of an instruction. The last exception is the fast interrupt exception, and it also makes sense to install it here. The fast interrupt handler can be stored directly at the 0x0000001C address without setting jump instructions, which can save a clock cycle and speed up the fast interrupt processing time.
The memory mapping address 0x00000000 is reserved for the vector table. In some processors, the vector table can be located at the high address 0xFFFF0000 [can be configured through coprocessor instructions]. In order to control memory access, today's operating system usually opens virtual memory. After virtual memory is turned on, the start space of memory is usually kernel process space and page table space, and the exception vector table can no longer be installed at address 0.
For example, the Cortex-A8 system supports setting the C12 register of CP15 to place the first address of the exception vector table at any address.
two。 Install exception vector table
We can install the exception vector table by simply using the following instructions:
B reset; jump into reset handler b HandleUndef; jump into undefined handler b HandSWI; jump into soft interrupt handler b HandPrefetchAbt; jump into prefetch instruction handler b HandDataAbt; jump into data access abort handler b HandNoUsed; jump into unused program b HandleIRQ; jump into interrupt handler b HandleFIQ; jump into fast interrupt handler
Usually after installing the exception vector table, jump to our own defined handler entrance, at this time we have not saved the scene of the interrupted program, so we have to save the interrupted program site in the entrance of the exception handler.
3. Save the execution site
At the beginning of the exception handler, to save the execution site of the interrupted program, the execution site of the program is nothing more than saving the data in the current operation register. You can save the site through the following stack operation instructions:
STMFD spaceborne particles, {R0-R12, LR_excep}
Note: LR_abt,SP_excep refers to LR and SP in the corresponding exception mode, and _ abt is added to facilitate readers' understanding.
It should be noted that when you jump to the entry of the exception handler, you have already switched to the corresponding exception mode, so the SP here is the SP_excep in the exception mode, so the site of the interrupted program (register data) is saved in the stack in the exception mode. The above instructions save all the R0~R12 to the exception mode stack, and finally save the return address of the interrupted program in the stack. The reason why the return address is saved is that the user program can be returned to continue execution in the future through instructions like: MOV PC, LR.
After the exception occurs, it is necessary to deal with the exception type, so each exception has its own exception handler, and the interrupt exception handling process is analyzed by the system interrupt handling in the next section.
5. Return of exception handling
After the exception handling is completed, return to the interrupted program to continue execution, as follows:
Recover the run-time register data of the interrupted program
Restore program run-time status CPSR
Return to the interrupted program to continue execution by entering the return address saved when the exception is entered
1. Exception return address
The execution of an instruction is divided into three main stages: fetch, decoding and execution. Due to the use of pipeline technology in CPU, the address of the current instruction execution should be PC-8 (four bytes of an instruction on a 32-bit computer), then the next instruction to execute the instruction should be PC-4. When an exception occurs, CPU automatically saves the value of PC-4 to LR, but whether the value is correct depends on the type of exception.
The return address of each mode is described as follows:
1. General / Quick interrupt request:
A quick interrupt request is handled the same as a general interrupt request return. Usually, after the processor executes the current instruction, it queries the FIQ/IRQ interrupt pin and checks to see if FIQ/IRQ interrupt is allowed. If an interrupt pin is valid and the system allows the interrupt to be generated, the processor will generate a FIQ/IRQ exception interrupt. When the FIQ/IRQ exception interrupt occurs, the value of the program counter pc has been updated, which points to the third instruction after the current instruction (for ARM instruction It points to the location of the current instruction address plus 12 bytes For the Thumb instruction, it points to the current instruction address plus 6 bytes). When an FIQ/IRQ exception interrupt occurs, the processor saves the value (pc-4) to the register lr_irq/lr_irq in the FIQ/IRQ exception mode, which points to the second instruction after the current instruction, so the correct return address can be calculated from the following instruction:
SUBS PC,LR_irq,#4; General interrupt SUBS PC,LR_fiq,#4; Fast interrupt
Note: LR_irq/LR_fiq is LR in general interrupt mode and fast interrupt exception mode respectively. There is no LR_xxx register. To facilitate readers to understand, add _ xxx, the same below.
two。 Prefetch refers to aborting exception:
During instruction prefetching, if the target address is illegal, the instruction is marked as a problematic instruction, at this time, the instruction before the instruction on the pipeline continues to execute, and when the instruction marked as problematic is executed, the processor generates an instruction prefetch abort exception interrupt. When an instruction prefetch exception interrupt occurs, the program returns to the problematic instruction, re-reads and executes the instruction, so the instruction prefetch abort exception interrupt should return to the instruction that produced the instruction prefetch abort exception interrupt. not the next instruction of the current instruction.
The instruction prefetch abort exception interrupt occurs when the currently executed instruction is executed in ALU. When the instruction prefetch abort exception interrupt occurs, the value of the program counter pc has not been updated, which points to the second instruction after the current instruction (for ARM instruction, it points to the location of the current instruction address plus 8 bytes; for Thumb instruction, it points to the location of the current instruction address plus 4 bytes). At this point, the processor saves the value (pc-4) to the lr_abt, which points to the next instruction of the current instruction, so the return operation can be achieved by the following instruction:
SUBS PC,LR_abt,#4
3. No instruction exception defined:
The undefined instruction exception interrupt occurs when the currently executed instruction is executed in ALU. When the undefined instruction exception interrupt occurs, the value of the program counter pc has not been updated. It points to the second instruction after the current instruction (for the ARM instruction, it points to the location of the current instruction address plus 8 bytes For the Thumb instruction, it points to the location of the current instruction address plus 4 bytes). When an undefined instruction exception interrupt occurs, the processor saves the value (pc-4) to the lr_und, and at this time (pc-4) points to the next instruction of the current instruction, so the undefined instruction exception interrupt return can be achieved by the following instruction:
MOV PC, LR_und
4. Soft interrupt instruction (SWI) exception:
SWI exception interrupts, like undefined exception interrupt instructions, are also generated when the currently executed instruction is executed in ALU. When the SWI instruction is executed, the value of pc has not been updated. It points to the second instruction after the current instruction (for ARM instruction, it points to the location of the current instruction address plus 8 bytes. For the Thumb instruction, it points to the current instruction address plus 4 bytes). When an undefined instruction exception interrupt occurs, the processor saves the value (pc-4) to the lr_svc, and at this time (pc-4) points to the next instruction of the current instruction, so the implementation method of returning from the SWI exception interrupt handling is the same as that of the undefined instruction exception interrupt handling:
MOV PC, LR_svc
5. Data abort exception:
When a data access exception interrupt occurs, the program should return to the problematic instruction and revisit the data, so the data access exception interrupt should return to the instruction that caused the data access abort exception interruption, not the next instruction of the current instruction. The data access exception interrupt occurs when the currently executed instruction is executed in ALU. When the data access exception interrupt occurs, the value of the program counter pc has been updated to point to the third instruction after the current instruction (for ARM instruction, it points to the location of the current instruction address plus 12 bytes; for Thumb instruction, it points to the location of the current instruction address plus 6 bytes). At this point, the processor saves the value (pc-4) to the lr_abt, which points to the second instruction after the current instruction, so the return operation can be achieved by the following instruction:
SUBS PC, LR_abt, # 8
When each of the above exceptions occurs, the return address should be repaired according to the specific exception type. "again, the return address of the interrupted program is saved in the LR_excep in the corresponding exception mode."
two。 Mode recovery
After the exception occurs, when entering the exception handler, the data in the user program register R0~R12 is saved in the stack in the exception mode. When the exception is handled and returned, the data stored in the stack should be restored to the original R0~R12.
There is no doubt that in the process of exception handling, the stack pointer SP_excep must be the same as the entry and exit of exception handling, otherwise the data restored to R0~R12 is incorrect, the execution scene is inconsistent when returning the interrupted program, and there is a problem. Although the execution site is restored, it is still in exception mode, and the state in CPSR is in exception mode.
Therefore, in order to restore the saved state in SPSR_excep to CPSR, SPSR_excep is the state of interrupted program execution. While restoring SPSR_excep to CPSR, the mode and state of CPU switch from abnormal mode to the mode and state of interrupted program execution.
At this time, the program site is restored, and the state is also restored, but the value in PC still points to the address space in the exception mode. We need to let CPU continue to execute the interrupted program, so we need to manually change the value of PC to the return address when entering the exception. The address has been calculated when the exception handling entry, and you can simply change the value of PC to LR_excep.
The above can be done step by step, but usually we can do all of the above with one instruction:
LDMFD spaceborne excitations, {r0-r12, pc} ^
Note: SP_excep is the SP in the corresponding exception mode, and the ^ symbol indicates that the SPSR_excep is restored to CPSR.
VI. The relationship between anomalies and patterns
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Reset exception enters SVC mode
Fiq fast interrupt request exception enters fast interrupt mode, supports high-speed data transmission and channel processing (this mode is entered in case of FIQ exception response)
Irq interrupt request exception enters interrupt mode, which is used for general interrupt handling (this mode is entered when IRQ exception response)
Prefetch prefetch refers to abort. Data abort exception enters abort mode to support virtual memory and / or memory protection.
Undef undefined instruction exception enters undefined mode and supports software emulation of hardware coprocessor (this mode is entered when instruction exception response is not defined)
Swi software interrupt, reset exception enter management mode, operating system protection code (enter this mode when system reset and software interrupt response)
7. Irq interrupt exception
1. The concept of interruption
What is interruption? we introduce it from an example of life. We were reading at home when suddenly the phone rang, you put down the book, answer the phone, talk to the caller, then put down the phone and come back to read your book. This is the phenomenon of "interruption" in life, that is, the normal working process is interrupted by external events.
In the processor, the so-called interrupt is a process, that is, during the normal execution of the program, the CPU encounters an external / internal emergency that needs to be dealt with, temporarily interrupts (suspends) the execution of the current program, and transfers to serve the event, and then returns to the pause (breakpoint) to continue execution of the original program. Programs that serve events are called interrupt service routines or interrupt handlers.
Strictly speaking, the above description is for interrupts caused by hardware events. Interruption can also be caused by software methods, that is, special instructions are arranged in the program in advance. When CPU executes such instructions, it transfers to execute the corresponding prearranged program, and then returns to execute the original program, which can be called soft interrupt. Taking soft interrupt into account, interrupt can be further defined: interrupt is a process in which CPU inserts another program running due to hardware or software reasons during the execution of the current program. The occurrence of interrupts caused by hardware is unpredictable, that is, random, while soft interrupts are arranged in advance.
two。 Interrupt handling flow
When an interrupt exception occurs, the entire processing flow:
As shown in the above figure:
1. Interruption occurs when execution is executed to 0x30000008
2.cpu performs 4 big steps and 3 small steps
1) Save CPSR to SPSR_irq 2) set the mode identification bit CPSR [4:0], CPU execution status CPSR [5]: t bit = 0 and turn off interrupt 3) set the return address LR=0x30000010 4) point the PC to the corresponding exception vector table address [interrupt IRQ:0x00000018]
3. After entering the exception vector table, execute the b instruction and jump to the exception handler
4. The exception handler needs to do the following
1) modify the return address SUBS PC,LR_irq,#4, that is, 0x3000000C 2) save the locale register 3) jump into the interrupt handler isr_proccess (), execute the interrupt handler 4) restore the locale register 5) return to the locale PC=LR
5. The program returns to the 0x3000000C position and continues to execute
A more detailed explanation of interrupts will be explained in more detail in subsequent articles, please follow [one Linux].
8. Swi exception
SWI instruction
The format of the SWI instruction is:
Immediate number of SWI {condition} 24 bits
The SWI instruction is used to generate software interrupts so that user programs can call system routines of the operating system. The operating system provides the corresponding system service in the exception handler of SWI. The immediate number of 24 bits in the instruction specifies the type of the system routine called by the user program, and the relevant parameters are passed through the general register. When the immediate number of 24 bits in the instruction is ignored, the type of the system routine called by the user program is determined by the content of the general register R0. At the same time, the parameters are passed through other general registers.
For example:
SWI 0x02; this instruction calls the system routine of operating system number bit 02.
The format of BKPT instruction BKPT instruction is: BKPT 16-bit immediate number BKPT instruction produces software breakpoint interrupt, which can be used for program debugging.
For example, the following is a code that contains the exception vector table. The program value fills in the entry of reset exception and swi exception, and other entry addresses can be filled in this position with the empty instruction nop.
Area first, code, readonly code32 entry; defined exception vector table vector b reset_handler; jump to reset_handler nop b swi_handler; address of SWI instruction exception jump nop swi_handler; swi handler code; exception handling should first store processor locale mrs R0, cpsr bic R0, R0, # 0x1f orr R0, R0, # 0x10 msr cpsr_c, R0; ldr R0, [lr, #-4] Get the machine code of the SWI instruction, the instruction before lr is the swi instruction with the subscript in the instruction; bic R0, R0, # 0xff000000; get SWI NUMBER movs pc, lr through the machine code; lr > pc and spsr-> cpsr returns SVC-> USER reset_handler; initialize the SVC mode stack ldr sp, = 0x40001000 Modify the current mode from SVC mode to USER mode mrs R0, cpsr bic R0, R0, # 0x1f orr R0, R0, # 0x10 msr cpsr_c, R0; initialize the USER mode stack ldr sp, = 0x40000800 mov R0, # 1; USER SWI swi 5; the statement open APP USER starts the exception by the user program itself; observe and record the changes of PC LR CPSR SPSR SP before and after the execution of the instruction And think about the changes that happen to the processor hardware automatically after the exception is generated. Add R1, R0, R0 stop b stop end
The running process is as follows:
Swi instruction execution
The main thing is to observe the changes of the mode before and after the implementation of swi, we can analyze it according to 4 big steps and 3 small steps.
Comparative changes before and after the execution of swi instructions
How to jump and switch modes at the same time? When returning from the swi exception, we need to perform two actions:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Copy the spsr to cpsr
Pc = lr jumps back to its original position
Both of these actions must be performed, but if the spsr is copied back step by step, the current mode changes back to usr mode, then the value of the corresponding lr becomes lr_usr, and the value of 0x0 [no bl instruction has been executed before], so how do you jump to it? We can use the following command
Movs pc, lr
This command performs two actions simultaneously:
Pc = lr cpsr = spsr returns SVC-> USER
Thus, it is possible to jump and switch modes at the same time. If the entry is already using ldm stack, you can reply with an instruction:
LDMFD spaceborne excitations, {r0-r12, pc} ^
See chapter 5.
How do I get a soft interrupt number?
1. To get the interrupt number of the swi instruction, we can only get the corresponding value from the machine code of swi
two。 In order to get the content of the swi instruction, we must first find the address of this instruction, and the value of lr is the address of the next instruction of the swi instruction, so we can get the soft interrupt number through the following code.
Ldr R0, [lr, #-4]; get the machine code of the SWI instruction, the instruction before lr is the swi instruction, and the subscript is bic R0, R0, # 0xff000000 in this instruction; get the SWI NUMBER through the machine code
System call and swi
System call
Linux applications have a lot of system calls, such as open,read,socket, which actually triggers swi exceptions, system calls sys_open,sys_read, and so on. The kernel performs specific operations according to the value of swi.
Each system call has its own unique number, and the identifier of the system call function is defined in the following file:
Linux/arch/arm/kernel/calls.S
The contents are as follows:
/ * 0 * / CALL (sys_restart_syscall) CALL (sys_exit) CALL (sys_fork) CALL (sys_read) CALL (sys_write). / CALL (sys_setns) CALL (sys_process_vm_readv) CALL (sys_process_vm_writev) CALL (sys_kcmp) CALL (sys_finit_module) # ifndef syscalls_counted .equ syscalls_padding, ((NR_syscalls + 3) & ~ 3)-NR_syscalls # define syscalls_counted # endif .rept syscalls_padding CALL (sys_ni_syscall) .endr
SWI snippet analysis
Search vector_swi and find the entry function
Arch\ arm\ kernel\ entry-common.S
.align 5 Y (vector_swi) @ Save site sub sp, sp, # S_FRAME_SIZE stmia sp, {R0-R12} @ Calling R0-R12 add R8, sp, # S_PC stmdb R8, {sp, lr} ^ @ Calling sp, lr mrs R8, spsr @ called from non-FIQ mode, so ok. Str lr, [sp, # S_PC] @ Save calling PC str R8, [sp, # S_PSR] @ Save CPSR str R0, [sp, # S_OLD_R0] @ Save OLD_R0 zero_fp @ get the instruction address of swi Make sure that the swi instruction ldr scno, [lr, #-4] @ get SWI instruction A710 (and ip, scno, # 0x0f000000 @ check for SWI) A710 (teq ip, # 0x0f000000) A710 (bne .Larm710bug) @ tbl equals the array table base address get_thread_info tsk adr tbl, sys_call_table @ load syscall table pointer ldr ip, [tsk # TI_FLAGS] @ check for syscall tracing @ clears the high 8-bit bic scno, scno, # 0xff000000 @ mask off SWI op-code @ # define _ _ NR_SYSCALL_BASE 0x900000 where the value of swi is actually 0x900000 0x900001. So clear this high position of 9 eor scno, scno, # _ _ NR_SYSCALL_BASE @ check OS number @ according to the index number Call function @ tbl in the array to tbl: array table base address, scno: index value of sys_write () to be called lsl # 2: move 2 bits to the left, a function pointer occupies 4 bytes of cmp scno, # NR_syscalls @ check upper syscall limit adr lr, ret_fast_syscall @ return address ldrcc pc, [tbl, scno, lsl # 2] @ call sys_* routine
1. Here we first get the contents of the swi instruction, and the swi instruction is located in lr-4 for the following reasons
two。 Then analyze it to make sure it's a swi instruction, that is,
And ip, scno, # 0x0f000000
3. Get the address of a global array containing system call functions
At this point, I believe you have a deeper understanding of "how to understand ARM handling exceptions". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.