diff --git a/Makefile b/Makefile
index f8c820e..f049217 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,8 @@ OBJS = \
   $K/sysfile.o \
   $K/kernelvec.o \
   $K/plic.o \
-  $K/virtio_disk.o
+  $K/virtio_disk.o \
+  $K/freeram.o
 
 # riscv64-unknown-elf- or riscv64-linux-gnu-
 # perhaps in /opt/riscv/bin
@@ -132,6 +133,7 @@ UPROGS=\
 	$U/_ln\
 	$U/_ls\
 	$U/_mkdir\
+	$U/_freeram\
 	$U/_rm\
 	$U/_sh\
 	$U/_stressfs\
diff --git a/kernel/defs.h b/kernel/defs.h
index d1b6bb9..5290161 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -187,3 +187,6 @@ void            virtio_disk_intr(void);
 
 // number of elements in fixed-size array
 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
+
+// freeram.c exercise 2.9.1
+uint64          freeram(void);
diff --git a/kernel/freeram.c b/kernel/freeram.c
new file mode 100644
index 0000000..73357ab
--- /dev/null
+++ b/kernel/freeram.c
@@ -0,0 +1,34 @@
+#include "types.h"
+#include "param.h"
+#include "memlayout.h"
+#include "spinlock.h"
+#include "riscv.h"
+#include "defs.h"
+
+struct run {
+  struct run *next;
+};
+
+extern struct {
+  struct spinlock lock;
+  struct run *freelist;
+} kmem;
+
+uint64
+sys_freeram(void)
+{
+  struct run *r;
+  uint64 free_pages = 0;
+
+  acquire(&kmem.lock);
+
+  // loop over free pages' list
+  r = kmem.freelist;
+  while (r != 0) {
+      free_pages++;
+      r = r->next;
+  }
+  release(&kmem.lock);
+
+  return free_pages * PGSIZE;
+}
diff --git a/kernel/syscall.c b/kernel/syscall.c
index ed65409..9663fa8 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -101,6 +101,7 @@ extern uint64 sys_unlink(void);
 extern uint64 sys_link(void);
 extern uint64 sys_mkdir(void);
 extern uint64 sys_close(void);
+extern uint64 sys_freeram(void); // exercise 2.9.1
 
 // An array mapping syscall numbers from syscall.h
 // to the function that handles the system call.
@@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = {
 [SYS_link]    sys_link,
 [SYS_mkdir]   sys_mkdir,
 [SYS_close]   sys_close,
+[SYS_freeram] sys_freeram, // exercise 2.9.1
 };
 
 void
diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..f4b89a6 100644
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -20,3 +20,4 @@
 #define SYS_link   19
 #define SYS_mkdir  20
 #define SYS_close  21
+#define SYS_freeram 22 // exercise 2.9.1
diff --git a/user/freeram.c b/user/freeram.c
new file mode 100644
index 0000000..6d7fa2b
--- /dev/null
+++ b/user/freeram.c
@@ -0,0 +1,36 @@
+#include "kernel/types.h"
+#include "kernel/stat.h"
+#include "kernel/riscv.h" // to get PGSIZE
+#include "user/user.h"
+
+#define KIBIBYTES 1024
+#define MEBIBYTES (1024 * 1024)
+void
+display_memory(void)
+{
+  uint64 free_ram, npages, nm, nk;
+  free_ram = freeram();
+  npages = free_ram / PGSIZE;
+  nm = free_ram / MEBIBYTES;
+  nk = free_ram / KIBIBYTES;
+  printf("%ld bytes (%ld MiB or %ld KiB), %ld pages  ", free_ram, nm, nk, npages);
+}
+
+int
+main(int argc, char *argv[])
+{
+  void *mem;
+
+  display_memory();
+  printf("free memory at the start\n");
+
+  mem = malloc(PGSIZE * 1000);
+  display_memory();
+  printf("after 1000*PGSIZE mem alloc\n");
+
+  free(mem);
+  display_memory();
+  printf("after dealloc\n");
+
+  exit(0);
+}
diff --git a/user/user.h b/user/user.h
index f16fe27..d4d1186 100644
--- a/user/user.h
+++ b/user/user.h
@@ -22,6 +22,7 @@ int getpid(void);
 char* sbrk(int);
 int sleep(int);
 int uptime(void);
+uint64 freeram(void); // exercise 2.9.1
 
 // ulib.c
 int stat(const char*, struct stat*);
diff --git a/user/usys.pl b/user/usys.pl
index 01e426e..9cea900 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,3 +36,4 @@ entry("getpid");
 entry("sbrk");
 entry("sleep");
 entry("uptime");
+entry("freeram");