diff --git a/matlab/dynare_config.m b/matlab/dynare_config.m
index 0644f8e7f7afefb437902c9eb232c1b8066bbe3e..98ed0c0bbec9565735c547e9e791d20b4657b9e5 100644
--- a/matlab/dynare_config.m
+++ b/matlab/dynare_config.m
@@ -93,11 +93,6 @@ if (exist('OCTAVE_VERSION') && ~user_has_octave_forge_package('statistics')) ...
     addpath([dynareroot '/missing/nanmean'])
 end
 
-% linsolve is missing in Octave
-if (exist('OCTAVE_VERSION'))
-    addpath([dynareroot '/missing/linsolve'])
-end
-
 % Add path to MEX files
 if exist('OCTAVE_VERSION')
     addpath([dynareroot '../mex/octave/']);
diff --git a/matlab/missing/linsolve/linsolve.m b/matlab/missing/linsolve/linsolve.m
deleted file mode 100644
index 2ae167bd4455b97020a3ae46ee3d113f5ea0e238..0000000000000000000000000000000000000000
--- a/matlab/missing/linsolve/linsolve.m
+++ /dev/null
@@ -1,33 +0,0 @@
-function [x,c] = linsolve(A,B,opts)
-% (very imperfect) Clone of Matlab's linsolve.
-
-% Copyright (C) 2010-2011 Dynare Team
-%
-% This file is part of Dynare.
-%
-% Dynare is free software: you can redistribute it and/or modify
-% it under the terms of the GNU General Public License as published by
-% the Free Software Foundation, either version 3 of the License, or
-% (at your option) any later version.
-%
-% Dynare is distributed in the hope that it will be useful,
-% but WITHOUT ANY WARRANTY; without even the implied warranty of
-% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-% GNU General Public License for more details.
-%
-% You should have received a copy of the GNU General Public License
-% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
-
-    c = [];
-    x = [];
-    if nargin == 3
-        if isfield(opts,'TRANSA')
-            A = A';
-        end
-    end
-    if nargout == 2
-        c = rcond(A);
-    end
-    
-    x = A\B;
-    
diff --git a/mex/build/octave/Makefile.am b/mex/build/octave/Makefile.am
index 132ddbe15889f4c84ed13e1e636454fdd66da581..2689afab4a680d4fec1573075bd34ff89cd2ca75 100644
--- a/mex/build/octave/Makefile.am
+++ b/mex/build/octave/Makefile.am
@@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I ../../../m4
 
 # libdynare++ must come before gensylv, k_order_perturbation, dynare_simul_
 if DO_SOMETHING
-SUBDIRS = mjdgges kronecker bytecode libdynare++ gensylv k_order_perturbation dynare_simul_ qzcomplex ordschur block_kalman_filter sobol local_state_space_iterations
+SUBDIRS = mjdgges kronecker bytecode libdynare++ gensylv k_order_perturbation dynare_simul_ qzcomplex ordschur block_kalman_filter sobol local_state_space_iterations linsolve
 
 if HAVE_GSL
 if HAVE_MATIO
diff --git a/mex/build/octave/configure.ac b/mex/build/octave/configure.ac
index da3c6e2ac7e6e0ada7ace30c78a3a899ffc50c7f..0a136279976f1667c1d903037faaacc6635b9ef0 100644
--- a/mex/build/octave/configure.ac
+++ b/mex/build/octave/configure.ac
@@ -132,6 +132,7 @@ AC_CONFIG_FILES([Makefile
                  ms_sbvar/Makefile
                  block_kalman_filter/Makefile
 		 sobol/Makefile
-		 local_state_space_iterations/Makefile])
+		 local_state_space_iterations/Makefile
+                 linsolve/Makefile])
 
 AC_OUTPUT
diff --git a/mex/build/octave/linsolve/Makefile.am b/mex/build/octave/linsolve/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..4f3a5aaf195c136db20d93a63286d9e666e172a5
--- /dev/null
+++ b/mex/build/octave/linsolve/Makefile.am
@@ -0,0 +1,6 @@
+EXEEXT = .oct
+include ../mex.am
+
+noinst_PROGRAMS = linsolve
+
+nodist_linsolve_SOURCES = $(top_srcdir)/../../sources/linsolve/linsolve.cc
diff --git a/mex/sources/Makefile.am b/mex/sources/Makefile.am
index 41b94ffa67d9bd6a879dbca859f322daabaacc53..57934b16407763c5867c47ed8cf96eddd53ab7ab 100644
--- a/mex/sources/Makefile.am
+++ b/mex/sources/Makefile.am
@@ -15,7 +15,8 @@ EXTRA_DIST = \
 	ms-sbvar \
 	block_kalman_filter \
 	sobol \
-	local_state_space_iterations
+	local_state_space_iterations \
+	linsolve
 
 clean-local:
 	rm -rf `find mex/sources -name *.o`
diff --git a/mex/sources/linsolve/linsolve.cc b/mex/sources/linsolve/linsolve.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e6329a28f74c6093f5589d70ee2a028d484b1c24
--- /dev/null
+++ b/mex/sources/linsolve/linsolve.cc
@@ -0,0 +1,120 @@
+/*
+ * Oct-file for bringing MATLAB's linsolve function to Octave.
+ *
+ * The implementation is incomplete:
+ * - it only knows about the TRANSA, LT, UT and SYM options
+ * - it only works with square matrices
+ * - it only works on double matrices (no single precision or complex)
+ *
+ * Written by Sebastien Villemot <sebastien.villemot@ens.fr>.
+ */
+
+/*
+ * Copyright (C) 2012 Dynare Team
+ *
+ * This file is part of Dynare.
+ *
+ * Dynare is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dynare is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+
+DEFUN_DLD(linsolve, args, nargout, "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} @var{x} = linsolve (@var{a}, @var{b})\n\
+@deftypefnx {Loadable Function} [ @var{x}, @var{r} ] = linsolve (@var{a}, @var{b})\n\
+@deftypefnx {Loadable Function} @var{x} = linsolve (@var{a}, @var{b}, @var{options})\n\
+@deftypefnx {Loadable Function} [ @var{x}, @var{r} ] = linsolve (@var{a}, @var{b}, @var{options})\n\
+\n\
+Solves the linear system @math{A*X = B} and returns @var{X}.\n\
+\n\
+Alternatively, if @var{options} is provided and has a field @code{TRANSA} equal \
+to @code{true}, then it solves the system @math{A'*X = B}.\n\
+\n\
+\n\Also, the @code{LT} field of @var{options} (resp. the @code{UT} field) can be set \
+to @code{true} to indicate that the matrix @var{a} is lower (resp. upper) \
+triangular; similarly, the @code{SYM} field can be set to @code{true} to \
+indicate that the matrix is symmetric.\n\
+\n\
+If requested, @var{r} will contain the reciprocal condition number.\n\
+@end deftypefn\n\
+")
+{
+  int nargin = args.length();
+  octave_value_list retval;
+
+  if (nargin > 3 || nargin < 2 || nargout > 2)
+    {
+      print_usage();
+      return retval;
+    }
+
+  Matrix A = args(0).matrix_value();
+  Matrix B = args(1).matrix_value();
+  if (error_state)
+    return retval;
+
+  dim_vector dimA = A.dims();
+  dim_vector dimB = B.dims();
+
+  if (dimA(0) != dimB(0))
+    {
+      error("linsolve: must have same number of lines in A and B");
+      return retval;
+    }
+
+  if (dimA(0) != dimA(1))
+    {
+      error("linsolve: rectangular A not yet supported");
+      return retval;
+    }
+
+  
+  MatrixType typA;
+  typA.mark_as_full();
+  
+  bool transa = false;
+  if (nargin == 3)
+    {
+      octave_scalar_map opts = args(2).scalar_map_value();
+      if (error_state)
+        return retval;
+
+      octave_value tmp = opts.contents("TRANSA");
+      transa = tmp.is_defined() && tmp.bool_matrix_value().elem(0);
+
+      tmp = opts.contents("UT");
+      if (tmp.is_defined() && tmp.bool_matrix_value().elem(0))
+        typA.mark_as_upper_triangular();
+
+      tmp = opts.contents("LT");
+      if (tmp.is_defined() && tmp.bool_matrix_value().elem(0))
+        typA.mark_as_lower_triangular();
+
+      tmp = opts.contents("SYM");
+      if (tmp.is_defined() && tmp.bool_matrix_value().elem(0))
+        typA.mark_as_symmetric();
+    }
+
+  double rcond;
+  octave_idx_type info;
+
+  retval(0) = A.solve(typA, B, info, rcond, NULL, true, transa ? blas_trans : blas_no_trans);
+
+  if (nargout == 2)
+    retval(1) = rcond;
+
+  return retval;
+}