Download as pdf or txt
Download as pdf or txt
You are on page 1of 18

ECE 391 Exam 1, Spring 2016

Wednesday, February 24th


Solutions
ECE 391, Exam 1

1. Short Answer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 points


(a) (2 points) Find the bug in the following snippet of code. (There should be only one.)
1 mp1 ioctl find :
2 pushl %ebp
3 movl %esp , %ebp
4 pushl %ebx
5 pushl %e s i
6 pushl %edi
7
8 # g e t arg : s t r u c t p t r in ebx
9 movl 8(%ebp ) , %ebx
10 cmpl $0 , %ebx
11 je m p 1 i o c t l r e t u r n n e g 1
12
13 # g e t l o c a t i o n from s t r u c t i n t o e c x
14 movzwl LOCATION(%ebx ) , %ecx
15 # Note : o r i g i n a l i n s t r u c t i o n was movl ,
16 # c l a r i f i c a t i o n i s s u e d d u r i n g exam
17
18 # get mp1 list head
19 movl m p 1 l i s t h e a d , %edx
20 # c h e c k f o r NULL
21 t e s t l %edx , %edx
22 jz mp1 ioctl return neg 1
23
24 # ...
25
26 xorl %eax , %eax
27 jmp m p 1 i o c t l r e t u r n
28
29 # ...
30
31 mp1 ioctl return neg 1 :
32 movl $ −1, %eax
33 mp1 ioctl return :
34 popl %edi
35 popl %e s i
36 popl %ebx
37 leave
38 ret

Solution: The command on line 14 accesses the blink structure which was passed as an argu-
ment to the system call. Therefore, it is dereferencing a user-level pointer, which is not safe to
do in kernel code. Instead, the user structure should first be copied to kernel memory using
copy_from_user

Points: /2 1 of 17 NetID:
Question 1 continues. . . ECE 391, Exam 1

(b) (3 points) Given the following function prototype, please write x86 assembly code to call this func-
tion? Assume BL=arg1, DX=arg2, ESI=arg3. Don’t worry about saving any registers.
void some func(char arg1, short arg2, int arg3);

Solution: The correct code should be:


pushl %e s i
pushl %edx
pushl %ebx
c a l l some func
addl $12 ,%esp
Note that all function arguments, regardless of size, are aligned to a 4-byte boundary in the
calling convention. In other words, the below code is wrong:
pushl %e s i
pushw %dx
pushb %bl
c a l l some func
addl $7 ,%esp
However since we did not cover this in sufficient detail in the class, we marked this version as
correct. You can also optionally ensure that the padding bytes are 0 by doing something like
movzbl %bl,%ebx or andl $0xff,%ebx, but this is not required to receive full points.

(c) (2 points) Given that EAX=1000, EDI=10, what memory address is calculated for the memory
operand 100(%EAX, %EDI, 4)?

Solution: 100 + 1000 + 4 · 10 = 1140

(d) (2 points) Given row number is 20, column number is 15, what is the memory offset from the start
of video memory in your mp1?

Solution: The correct formula is 2 · (20 · 80(screen width) + 15) = 3230


We would give partial credit to 2 · (19 · 80 + 14) = 3070 for those who thought that the rows and
columns are numbered starting with 1 (which is inconsistent with how they have been used in
the course). No credit if rows start with 1 and columns start with 0.

(e) (2 points) What if the row number is 25, column number is 0?

Solution: This is off the screen because rows are 0–24. (If using rows and columns were
numbered starting with 1, this would still be invalid because column 0 would not exist.)

Points: /9 2 of 17 NetID:
ECE 391, Exam 1

2. Assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 points
In MP1, you were required to write a tasklet function to traverse the linked list and update accord-
ingly. Now, a different tasklet function needs to be implemented, and there are two things you need
to accomplish: (1), switch the on char and off char for each mp1 blink struct; (2) calculate the sum
of all countdown fields(dont ask why we want this). Gladly, a recursive helper function ‘traverse’ is
already implemented for you in C, and the return value should contain the sum of all countdown fields
in the given linked list starting at ‘node’. Your task is to implement the ‘do operation’ function in x86
assembly, and translate the ‘traverse’ function to x86 assembly.
/∗ a h e l p e r f u n c t i o n t h a t w i l l be c a l l e d i n t h e new t a s k l e t ,
∗ eg . t r a v e r s e ( m p 1 l i s t h e a d ) ; ∗/
int t r a v e r s e ( struct m p 1 b l i n k s t r u c t ∗ node ) {
i f ( node != NULL) {
return d o o p e r a t i o n ( node)+ t r a v e r s e ( node−>next ) ;
}
return 0 ;
}

/∗ f u n c t i o n s i g n a t u r e f o r d o o p e r a t i o n ∗/
int d o o p e r a t i o n ( struct m p 1 b l i n k s t r u c t ∗ node ) ;

/∗ i n f o r m a t i o n t h a t may h e l p you f o r t h e problem ∗/


struct m p 1 b l i n k s t r u c t {
unsigned short l o c a t i o n ; /∗ L i n e a r o f f s e t on t e x t −mode b u f f e r ∗/
char o n c h a r ; /∗ Char t o p u t d u r i n g ”on” p e r i o d ∗/
char o f f c h a r ; /∗ Char t o p u t d u r i n g ” o f f ” p e r i o d ∗/
unsigned short o n l e n g t h ; /∗ Length o f on p e r i o d ∗/
unsigned short o f f l e n g t h ; /∗ Length o f o f f p e r i o d ∗/
unsigned short countdown ; /∗ Number o f RTC i n t e r r u p t s l e f t i n p e r i o d ∗/
unsigned short s t a t u s ; /∗ S t a t u s word ( on=1/ o f f =0) ∗/
struct m p 1 b l i n k s t r u c t ∗ next ; /∗ p o i n t e r t o n e x t item i n l i n k e d l i s t ∗/
}

# U s e f u l o f f s e t c o n s t a n t s f o r a c c e s s i n g members o f a
# struct mp1 blink struct structure
LOCATION = 0
ON CHAR = 2
OFF CHAR = 3
ON LENGTH = 4
OFF LENGTH = 6
COUNTDOWN = 8
STATUS = 10
NEXT = 12
STRUCT SIZE = 16

Points: /0 3 of 17 NetID:
Question 2 continues. . . ECE 391, Exam 1

(a) (8 points) Implement the ‘do operation’ function in x86 assembly. It should (1) switch the on char
and off char of the provided node and (2) return the countdown of the provided node as int. For
symplicity, you DO NOT need to check if node is NULL. We provided the equivalent C code below.
Your code should not exceed 15 lines, excluding comments and labels
int d o o p e r a t i o n ( struct m p 1 b l i n k s t r u c t ∗ node ) {
char tmp ;
tmp = node−>o n c h a r ;
node−>o n c h a r = node−>o f f c h a r ;
node−>o f f c h a r = tmp ;
return node−>countdown ;
}

do operation :

Solution:
# s e t up s t a c k frame
pushl %ebp
movl %esp ,%ebp

# g e t node argument
movl 8(%ebp) ,%ecx
# s a v e ON CHAR, OFF CHAR
movb ON CHAR(%ecx) ,% dl
movb OFF CHAR(%ecx) ,%dh
# r e p l a c e them
movb %dh ,ON CHAR(%ecx )
movb %dl ,OFF CHAR(%ecx )
# r e t u r n countdown , ze r o −e x t e n d t o 32 b i t s
movzwl COUNTDOWN(%ecx) ,%eax
# exit function
leave
ret

Points: /8 4 of 17 NetID:
Question 2 continues. . . ECE 391, Exam 1

(b) (12 points) Translate the ‘traverse’ function to x86 assembly. Part of the assembly code is given so
you just need to fill in the blanks. And remember, the return value should contain the sum of all
countdown fields in the given linked list starting at ‘node’.
1 traverse :
2 pushl %ebp
3 movl %esp , %ebp
4
5 #s a v e c a l l e e −s a v e d r e g i s t e r
6 pushl %ebx
7
8 # local variable
9 subl $4 , %esp
10
11 #c h e c k node f o r NULL
12 movl 8(%ebp ) , %ebx
13 cmpl 0 , %ebx
14 je NULL PTR
15
16 #perform t h e o p e r a t i o n
17 pushl %ebx
18 call do operation
19 addl $4 , %esp
20 movl %eax , -8(%ebp )
21
22 #r e c u r s i v e l y c a l l t r a v e r s e
23 movl NEXT(%ebx) , %eax
24 pushl %eax
25 call traverse
26 addl $4 , %esp
27
28 #c a l c u l a t e sum o f countdown
29 addl -8(%ebp) , %eax
30 jmp RETURN
31
32 NULL PTR :
33 xorl %eax , %eax
34
35 RETURN:
36 addl $4 , %esp
37 popl %ebx
38 leave
39 ret

Points: / 12 5 of 17 NetID:
Question 2 continues. . . ECE 391, Exam 1

(c) (8 points) Ben Bitdiddle has found an old page on the Internet, written by someone with the
username psimon41, called 50 Ways to Leave Your Function. He’s getting a bit smarter and realizes
that you can’t trust everything you read on the Internet; can you tell him which versions are going
to be a correct replacement to lines 36–39 in the previous part? For each part, either write “correct”
or explain why the replacement is wrong.
i. # J u s t s w i t c h o u t t h e frame , Wayne
l e a l −4(%ebp) ,% esp
popl %ebx
leave
ret

Solution: Correct

# Clean up t h e s t a c k , Jack
popl %ebx
popl %ebx
leave
ret

Solution: Correct

iii.
ii. # If t h e r e ’ s some space , Chase ,
addl $4 ,%esp
popl %ebx
popl %ebp
ret

Solution: Correct

iv. # That you have t o f r e e


movl (%ebp) ,%ebp
popl %ebx
popl %ecx
ret

Solution: Incorrect. The local variable is saved into %ebx, saved %ebx is saved into %ecx, and
saved %ebp is used as a return value.

v. # Return from t h e c a l l , S a u l
movl 4(%esp ) ,%ebx
movl 8(%esp ) ,%ebp
addl $8 ,%esp
ret

Solution: Incorrect. %esp needs to be adjusted by 12, not 8, to clear off saved %ebp, %ebx, and
local variable.

vi. # Don ’ t f o r g e t t o r e s t o r e r e g s !
movl 4(%ebp) ,%ebx
movl %ebp,%esp
popl %ebp
ret

Points: /8 6 of 17 NetID:
Question 2 continues. . . ECE 391, Exam 1

Solution: Incorrect. 4(%ebp) is the return address; to restore %ebx you need to say -4(%ebp).

vii. # Put t h e r e t u r n a d d r e s s , J e s s
movl 4(%esp ) ,%ebx
leave
ret

Solution: Correct

viii. # i n t o EIP !
addl $4 , %esp
popl %ebx
leave
popl %ecx
jmp ∗%ecx

Solution: Correct

ix. (2 points (bonus)) Explain why this is wrong:


leave
movl −8(%esp ) ,%ebx
ret

Solution: This will work fine most of the time, however, after the first instruction, the value of
%ebx is saved on the part of the stack (numerically) below %esp. If this code runs in kernel mode
and an interrupt occurs, that part of the stack will be overwritten by the saved processor state.
In user mode, this is not an issue because the interrupt will use the kernel stack, but a deliver
of a signal (a user-level analog of an interrupt, discussed later i the course) will have a similar
bad result. In general, you cannot assume that any part of the stack (numerically) below %esp is
preserved for you.

Points: /0 7 of 17 NetID:
ECE 391, Exam 1

3. PIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 points
For this question, you should assume that the master and slave PIC are configured as shown on the
reference sheet at the back of the exam.
(a) (4 points) Consider the following state of the registers on the master and slave PICs. List which
interrupt request lines would, if activated, cause an interrupt to be signaled to the CPU. (E.g., slave
IR2, master IR7)
• Master Interrupt Mask Register (IMR): bits 1 and 5 set
• Master In-service Register (ISR): bit 4 set
• Slave IMR: bits 2 and 3 set
• Slave ISR: bit 5 set
• Master and slave Interrupt Request Register (IRR): all bits clear

Solution: IR0 and IR3 on the master (IR1 is masked, IR4 is in service, and IR5–IR7 are lower
priority).
IR0, IR1, and IR4 on the slave (IR2 and IR3 are masked, IR5 is in service, and IR6,IR7 are
lower priority).
Note: The question did not intend to ask about IR2 on the master since it’s hooked up to the
slave, rather than a device, but technically if IR2 on the master is raised, it will generate an
interrupt, so we will accept master IR2.
A lot of people thought that any interrupt being in service on the master would preempt IR3–
IR7 on the master. The mechanism that makes this work is the in-service bit on IR2 on the
master, which is set after a slave interrupt (see next part), but is not the case here.

(b) (12 points) Assume that all registers on the master and slave are set to 0. Walk through the steps
that happen after an interrupt is signaled on IR4 on the slave, using the potential steps on the
next page, up to (and including) the execution of the mask_and_ack function. (We’ve reproduced
a simplified version of the function below for your reference.)
#define MASTER CMD 0 x20
#define MASTER IMR 0 x21
#define SLAVE CMD 0xA0
#define SLAVE IMR 0xA1

void mask and ack ( int i r q ) {


unsigned char curmask ;
i f ( i r q >= 8 ) { /∗ s l a v e ∗/
int s l a v e i r q = i r q & 7 ;
curmask = i n b (SLAVE IMR ) ;
outb ( curmask | ( 1 << s l a v e i r q ) , SLAVE IMR ) ;
/∗ s p e c i f i c EOI ∗/
outb ( 0 x60 | s l a v e i r q , SLAVE CMD ) ;
} else {
curmask = i n b (MASTER IMR ) ;
outb ( curmask | ( 1 << i r q ) , MASTER IMR ) ;
outb ( 0 x60 | i r q , MASTER CMD) ;
}
}

Points: / 16 8 of 17 NetID:
Question 3 continues. . . ECE 391, Exam 1

Solution:

1. The slave sets bit 4 of the IRR register to 1


2. The slave raises the INT line
3. The master sets bit 2 of the IRR register to 1
4. The master raises the INT line
5. The master writes 2 to the CAS bus
6. The CPU strobes the INTA line
7. The master sets bit 2 of the ISR register to 1
8. The master sets bit 2 of the IRR register to 0
9. The slave writes vector 0x2c to the data bus
10. The slave sets bit 4 of the ISR register to 1
11. The slave sets bit 4 of the IRR register to 0
12. The CPU reads value 0x00 from port 0xA1
13. The CPU writes value 0x10 to port 0xA1
14. The slave sets bit 4 of the IMR register to 1
15. The CPU writes value 0x64 to port 0xA0
16. The slave sets bit 4 of the ISR register to 0

Note: steps 7–11 are essentially simultaneous and so their order was not graded. The order of
5 was also fairly flexible.

Points: /0 9 of 17 NetID:
Question 3 continues. . . ECE 391, Exam 1

(c) (3 points) The version of mask_and_ack above is a little too simplified. Explain what is missing
and describe the impact it will have on interrupt processing.

Solution: The function does not send an EOI to the master, which means that the ISR for
IR2 remains on. This will prevent any interrupts from the slave, as well as IR3–IR7 on the
master, from ever being delivered to the CPU.
Note: some people mentioned the lack of spinlocks. We will give credit for this answer only if you
provide a correct explanation of what the impact of the missing spinlock is: two simultaneous
mask_and_ack invocations could both read the IMR and then write the updated mask back to
IMR, resulting in one of the bits not being masked.

(d) (2 points) Suppose that the device on IR4 slave line signals another interrupt. When would the
interrupt have to arrive in your sequence in the part above for it to be missed? (I.e., your answer
should be in the form of “before step 6.”)

Solution: Before step 11, when the slave resets bit 4 of IRR to 0. If the second interrupt
occurs after this step, the IRR bit will get set back to 1 and the next interrupt will get signaled
after the corresponding ISR and IMR bits get cleared.

Points: /5 10 of 17 NetID:
ECE 391, Exam 1

4. Stack and Calling Convention . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 points


Your friend Ben is fascinated with function pointers but thinks the C language restricts his creativity.
He wrote the apply subroutine to invoke a series of functions as shown below.
The apply function is called as follows:

w = apply(x, 3, foo, bar, baz)

This is equivalent to:

y = baz(x)
z = bar(y, x)
w = foo(z, y, x)

To test his creation, Ben wrote a series of small subroutines that are invoked by the magic subroutine.
The code is given on this page and the next. The following C declarations correspond to the subroutines
that are given in assembly.

int apply(int input, int nptrs, ...);


int constadd(int, ...);
int shiftby7(int, ...);
int splat(int, ...);
int threehalves(int, ...);
int decrement(int, ...);
int half(int, ...);

Implementations of all of the functions are given below and on the next page.

. g l o b a l apply int p r i n t s t a c k ( int x , int y )


apply : {
pushl %ebp int ∗ s t a c k = &x − 1 ;
movl %esp , %ebp f o r ( int i = 0 ; i < x + 7 ; i ++) {
movl 8(%ebp ) , %eax p r i n t f ( ”%x:%x\n” ,
movl 12(%ebp ) , %ebx stack + i ,
∗( stack + i ) ) ;
more : }
cmpl $0 , %ebx return x ;
jz done }

pushl %eax
call ∗12(%ebp , %ebx , 4 )
return to apply :
decl %ebx
jmp more

done :
leave
ret

int magic ( int x )


{
return apply ( x , 7 , constadd , s h i f t b y 7 , s p l a t , p r i n t s t a c k ,
t h r e e h a l v e s , decrement , h a l f ) ;
}

Points: /0 11 of 17 NetID:
Question 4 continues. . . ECE 391, Exam 1

(a) (8 points) Suppose that a breakpoint is set on the first instruction in print stack. Now, a test
program calls magic(16). Complete the tables below by writing the values on the stack and the
value of EBP at the time the breakpoint is reached. Use labels instead of numbers where possible.

The label return to magic points to the instruction that immediately follows the call instruction
inside magic.
Hint: You do not need to understand every function to complete the table.

.global half Final ESP 0xffffd928


half :
movl 4(%esp ) , %eax Final EBP 0xffffd93c
sarl $1 , %eax
ret
. g l o b a l decrement
decrement :
movl 4(%esp ) , %eax Address Value
decl %eax
ret 0xffffd928 return to apply
.global threehalves
threehalves : 0xffffd92c 12
movl 8(%esp ) , %eax
sarl $1 , %eax 0xffffd930 7
orl 8(%esp ) , %eax
ret 0xffffd934 8
.global splat
splat : 0xffffd938 16
movl $0x101000 , %eax
imul 4(%esp ) , %eax 0xffffd93c 0xffffd968
addl $0x11 , %eax
ret 0xffffd940 return to magic
.global shiftby7
shiftby7 : 0xffffd944 16
movl 16(%esp ) , %eax
shll $7 , %eax 0xffffd948 7
ret
. g l o b a l constadd 0xffffd94c constadd
constadd :
movl $0x2c2000 , %eax 0xffffd950 shiftby7
addl 4(%esp ) , %eax
addl 8(%esp ) , %eax 0xffffd954 splat
ret
0xffffd958 print stack
Solution: 1 pt each for the 6
blanks above return to magic. 2 0xffffd95c threehalves
pt total for the rest.
Partial credit was given if a correct 0xffffd960 decrement
sequence of entries was written in
the wrong boxes. 0xffffd964 half

0xffffd968 0xffffd9c8

Points: /8 12 of 17 NetID:
Question 4 continues. . . ECE 391, Exam 1

(b) (2 points) Ben’s program crashes when magic is called from some places in the program but not
others. What mistake did Ben make in apply?

Solution:
EBX is used by apply but not saved and restored.

Partial credit was only given if the correct answer was followed by an incorrect statement.

A lot of people thought that the stack could overflow but that cannot occur when magic is
called because it has a fixed list of arguments.

(c) (2 points (bonus)) Bonus: What is the return value of magic(16)? Write the value of EAX each
time the processor reaches return to apply for full credit. Express your answer in hexadecimal.
Hint: Continue the work you did in Part A.

Solution:
0x8
0x7
0xc
0xc
0xc0c011
0x380
0xece391

Points: /2 13 of 17 NetID:
ECE 391, Exam 1

5. Synchronization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 points
(a) (10 points) Below are some examplesystem calls that run in kernel space, K1–K5. Some of these
system calls also use a helper function F1. Finally, I1 is an interrupt handler. These different parts
all access some shared variables indicated by G1-G5. You must add spinlocks to the code below
to guarantee safety. A large number of locks have been declared and initialized for you. They
are accessible by global pointers L1, L2, L3, etc. You You should assume that the code below is
complete. For example, F1 will not be called in other places, so you should optimizes with respect
to the shown code below. You can also assume that the system calls are running with interrupts
enabled.
In addition to ensuring the safety of the code, you are also required to ensure that the maximum
amount of parallelism can be achieved with your locking system. While doing this, you must use the
minimum number of locks possible while still achieving safety and maximum parallelism. Finally,
you should select the appropriate primitive (spin_lock or spin_lock_irqsave) for each use. (Note
that in class we said that it’s always safe to use spin_lock_irqsave, but here we want you to pick
the primitive that does the minimum work necessary.)
/∗ s h a r e d v a r i a b l e s ∗/
int G1 , G2 , G3 , G4 , G5 ;
/∗ s p i n l o c k s ; assume t h e y ’ r e i n i t i a l i z e d p r o p e r l y ∗/
s p i n l o c k t L1 , L2 , L3 , L4 , L5 , L6 , L7 , L8 ;
/∗ some macros t o s a v e s p a c e ∗/
#define SL ( l o c k ) s p i n l o c k (& l o c k )
#define SU( l o c k ) s p i n u n l o c k (& l o c k )
#define SLI ( l o c k , f l a g s ) s p i n l o c k i r q s a v e (& l o c k , f l a g s )
#define SUI ( l o c k , f l a g s ) s p i n u n l o c k i r q r e s t o r e (& l o c k , f l a g s )

void K1( ) {
SL(L1);

G1++;
G4++;

SU(L1);
}

void K2( ) {
int flags;
SLI(L2,flags);

G2++;
F1 ( ) ;

SUI(L2,flags);
}

Points: / 10 14 of 17 NetID:
Question 5 continues. . . ECE 391, Exam 1

void K3( ) {
SL(L1);
SL(L3);

G1++;
G3++;
G4++;

SU(L3);
SU(L1);
}

void K5( ) {
SL(L3);

G3++;

SU(L3);
}

void F1 ( ) {

G5++;

void I 1 ( ) {
SL(L2);

G2++;
G5++;

SU(L2);
}

Points: /0 15 of 17 NetID:
Question 5 continues. . . ECE 391, Exam 1

(b) (5 points) Now consider that there are 5 threads T1–T5 (unrelated to part (a)) that use the fol-
lowing locking order.

T1 T2 T3 T4 T5
on (L4) on (L1) on (L4) on (L6) on (L5)
on (L3) on (L2) on (L3) on (L5) on (L6)
on (L2) on (L3) on (L2) on (L4) off(L6)
on (L1) on (L4) on (L1) on (L3) off(L5)
off(L1) off(L4) off(L4) off(L3) ——-
off(L2) off(L3) off(L3) off(L4) ——-
off(L3) off(L2) off(L2) off(L5) ——-
off(L4) off(L1) off(L1) off(L6) ——-

Below mark what thread PAIRS can exhibit deadlock by filling in the chart with a “D”. Leave
safe combinations blank.

T1 T2 T3 T4 T5
T1 XX D
T2 XX XX D D
T3 XX XX XX
T4 XX XX XX XX D
T5 XX XX XX XX XX

(c) (3 points) Recall that spin_lock_irqsave calls CLI first and then locks the lock. Now consider
the function spin_lock_irqrestore: does it matter in which order it restores flags and unlocks
the lock? Give a brief reasoning to justify your answer.

Solution: It must release the lock first before restoring flags. Otherwise it will be holding the
lock with the interrupts enabled; if an interrupt occurs that tries to acquire the same lock, a
deadlock will occur.

Points: /8 16 of 17 NetID:
Question 5 continues. . . ECE 391, Exam 1

(d) (3 points) Recall that the lock prefix makes any x86 instruction atomic. Is the code below a working
implementation of a spinlock? Explain why or why not.
1 spin lock :
2 l o c k movl 4(% e s p ) , %eax
3 loop :
4 l o c k movl (%eax ) , %edx
5 lock test %edx , %edx
6 lock jnz loop
7 l o c k movl $1 , (%eax )
8 lock ret

Solution: The lock prefix ensures that each individual instruction is atomic. However, one
thread could execute instruction 4, reading the lock value as 0, after another thread executes
the same instruction, but before the second thread writes 1 to the lock in instruction 7, which
would leave both threads to assume they have the lock.

(e) (2 points) In general, why don’t we use the lock prefix in front of every single instruction?

Solution: The prefix locks the memory bus, which prevents other memory instructions from
executing at the same time; this significantly slows down execution.

Points: /5 17 of 17 NetID:

You might also like